C++類(lèi)和對(duì)象
C++ 在 C 語(yǔ)言的基礎(chǔ)上增加了面向?qū)ο缶幊蹋珻++ 支持面向?qū)ο蟪绦蛟O(shè)計(jì)。類(lèi)是 C++ 的核心特性,通常被稱(chēng)為用戶(hù)定義的類(lèi)型。
類(lèi)用于指定對(duì)象的形式,它包含了數(shù)據(jù)表示法和用于處理數(shù)據(jù)的方法。類(lèi)中的數(shù)據(jù)和方法稱(chēng)為類(lèi)的成員。函數(shù)在一個(gè)類(lèi)中被稱(chēng)為類(lèi)的成員。
C++類(lèi)定義
定義一個(gè)類(lèi),本質(zhì)上是定義一個(gè)數(shù)據(jù)類(lèi)型的藍(lán)圖。這實(shí)際上并沒(méi)有定義任何數(shù)據(jù),但它定義了類(lèi)的名稱(chēng)意味著什么,也就是說(shuō),它定義了類(lèi)的對(duì)象包括了什么,以及可以在這個(gè)對(duì)象上執(zhí)行哪些操作。
類(lèi)定義是以關(guān)鍵字 class 開(kāi)頭,后跟類(lèi)的名稱(chēng)。類(lèi)的主體是包含在一對(duì)花括號(hào)中。類(lèi)定義后必須跟著一個(gè)分號(hào)或一個(gè)聲明列表。例如,我們使用關(guān)鍵字 class 定義 Box 數(shù)據(jù)類(lèi)型,如下所示:
class Box
{
public:
double length; // 盒子的長(zhǎng)度
double breadth; // 盒子的寬度
double height; // 盒子的高度
};
關(guān)鍵字 public 確定了類(lèi)成員的訪問(wèn)屬性。在類(lèi)對(duì)象作用域內(nèi),公共成員在類(lèi)的外部是可訪問(wèn)的。您也可以指定類(lèi)的成員為 private 或 protected,這個(gè)我們稍后會(huì)進(jìn)行講解。
C++對(duì)象定義
類(lèi)提供了對(duì)象的藍(lán)圖,所以基本上,對(duì)象是根據(jù)類(lèi)來(lái)創(chuàng)建的。聲明類(lèi)的對(duì)象,就像聲明基本類(lèi)型的變量一樣。下面的語(yǔ)句聲明了類(lèi) Box 的兩個(gè)對(duì)象:
Box Box1; // 聲明 Box1,類(lèi)型為 Box
Box Box2; // 聲明 Box2,類(lèi)型為 Box
對(duì)象 Box1 和 Box2 都有它們各自的數(shù)據(jù)成員。
訪問(wèn)數(shù)據(jù)成員
類(lèi)的對(duì)象的公共數(shù)據(jù)成員可以使用直接成員訪問(wèn)運(yùn)算符 (.) 來(lái)訪問(wèn)。為了更好地理解這些概念,讓我們嘗試一下下面的實(shí)例:
#include <iostream>
using namespace std;
class Box
{
public:
double length; // 長(zhǎng)度
double breadth; // 寬度
double height; // 高度
};
int main( )
{
Box Box1; // 聲明 Box1,類(lèi)型為 Box
Box Box2; // 聲明 Box2,類(lèi)型為 Box
double volume = 0.0; // 用于存儲(chǔ)體積
// box 1 詳述
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 7.0;
// box 2 詳述
Box2.height = 10.0;
Box2.length = 12.0;
Box2.breadth = 13.0;
// box 1 的體積
volume = Box1.height * Box1.length * Box1.breadth;
cout << "Box1 的體積:" << volume <<endl;
// box 2 的體積
volume = Box2.height * Box2.length * Box2.breadth;
cout << "Box2 的體積:" << volume <<endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Box1 的體積:210
Box2 的體積:1560
需要注意的是,私有的成員和受保護(hù)的成員不能使用直接成員訪問(wèn)運(yùn)算符 (.) 來(lái)直接訪問(wèn)。我們將在后續(xù)的教程中學(xué)習(xí)如何訪問(wèn)私有成員和受保護(hù)的成員。
類(lèi)和對(duì)象詳解
類(lèi)成員函數(shù)
類(lèi)的成員函數(shù)是指那些把定義和原型寫(xiě)在類(lèi)定義內(nèi)部的函數(shù),就像類(lèi)定義中的其他變量一樣。類(lèi)成員函數(shù)是類(lèi)的一個(gè)成員,它可以操作類(lèi)的任意對(duì)象,可以訪問(wèn)對(duì)象中的所有成員。
讓我們看看之前定義的類(lèi) Box,現(xiàn)在我們要使用成員函數(shù)來(lái)訪問(wèn)類(lèi)的成員,而不是直接訪問(wèn)這些類(lèi)的成員:
class Box
{
public:
double length; // 長(zhǎng)度
double breadth; // 寬度
double height; // 高度
double getVolume(void);// 返回體積
};
成員函數(shù)可以定義在類(lèi)定義內(nèi)部,或者單獨(dú)使用范圍解析運(yùn)算符 :: 來(lái)定義。在類(lèi)定義中定義的成員函數(shù)把函數(shù)聲明為內(nèi)聯(lián)的,即便沒(méi)有使用 inline 標(biāo)識(shí)符。所以您可以按照如下方式定義 Volume() 函數(shù):
class Box
{
public:
double length; // 長(zhǎng)度
double breadth; // 寬度
double height; // 高度
double getVolume(void)
{
return length * breadth * height;
}
};
您也可以在類(lèi)的外部使用范圍解析運(yùn)算符 :: 定義該函數(shù),如下所示:
double Box::getVolume(void)
{
return length * breadth * height;
}
在這里,需要強(qiáng)調(diào)一點(diǎn),在 :: 運(yùn)算符之前必須使用類(lèi)名。調(diào)用成員函數(shù)是在對(duì)象上使用點(diǎn)運(yùn)算符(.),這樣它就能操作與該對(duì)象相關(guān)的數(shù)據(jù),如下所示:
Box myBox; // 創(chuàng)建一個(gè)對(duì)象
myBox.getVolume(); // 調(diào)用該對(duì)象的成員函數(shù)
讓我們使用上面提到的概念來(lái)設(shè)置和獲取類(lèi)中不同的成員的值:
#include <iostream>
using namespace std;
class Box
{
public:
double length; // 長(zhǎng)度
double breadth; // 寬度
double height; // 高度
// 成員函數(shù)聲明
double getVolume(void);
void setLength( double len );
void setBreadth( double bre );
void setHeight( double hei );
};
// 成員函數(shù)定義
double Box::getVolume(void)
{
return length * breadth * height;
}
void Box::setLength( double len )
{
length = len;
}
void Box::setBreadth( double bre )
{
breadth = bre;
}
void Box::setHeight( double hei )
{
height = hei;
}
// 程序的主函數(shù)
int main( )
{
Box Box1; // 聲明 Box1,類(lèi)型為 Box
Box Box2; // 聲明 Box2,類(lèi)型為 Box
double volume = 0.0; // 用于存儲(chǔ)體積
// box 1 詳述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// box 2 詳述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// box 1 的體積
volume = Box1.getVolume();
cout << "Box1 的體積:" << volume <<endl;
// box 2 的體積
volume = Box2.getVolume();
cout << "Box2 的體積:" << volume <<endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Box1 的體積: 210
Box2 的體積: 1560
類(lèi)訪問(wèn)修飾符
數(shù)據(jù)封裝是面向?qū)ο缶幊痰囊粋€(gè)重要特點(diǎn),它防止函數(shù)直接訪問(wèn)類(lèi)類(lèi)型的內(nèi)部成員。類(lèi)成員的訪問(wèn)限制是通過(guò)在類(lèi)主體內(nèi)部對(duì)各個(gè)區(qū)域標(biāo)記 public、private、protected 來(lái)指定的。關(guān)鍵字 public、private、protected 稱(chēng)為訪問(wèn)修飾符。
一個(gè)類(lèi)可以有多個(gè) public、protected 或 private 標(biāo)記區(qū)域。每個(gè)標(biāo)記區(qū)域在下一個(gè)標(biāo)記區(qū)域開(kāi)始之前或者在遇到類(lèi)主體結(jié)束右括號(hào)之前都是有效的。成員和類(lèi)的默認(rèn)訪問(wèn)修飾符是 private。
class Base {
public:
// 公有成員
protected:
// 受保護(hù)成員
private:
// 私有成員
};
公有(public)成員
公有成員在程序中類(lèi)的外部是可訪問(wèn)的。您可以不使用任何成員函數(shù)來(lái)設(shè)置和獲取公有變量的值,如下所示:
#include <iostream>
using namespace std;
class Line
{
public:
double length;
void setLength( double len );
double getLength( void );
};
// 成員函數(shù)定義
double Line::getLength(void)
{
return length ;
}
void Line::setLength( double len )
{
length = len;
}
// 程序的主函數(shù)
int main( )
{
Line line;
// 設(shè)置長(zhǎng)度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;
// 不使用成員函數(shù)設(shè)置長(zhǎng)度
line.length = 10.0; // OK: 因?yàn)?length 是公有的
cout << "Length of line : " << line.length <<endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Length of line : 6
Length of line : 10
私有(private)成員
私有成員變量或函數(shù)在類(lèi)的外部是不可訪問(wèn)的,甚至是不可查看的。只有類(lèi)和友元函數(shù)可以訪問(wèn)私有成員。
默認(rèn)情況下,類(lèi)的所有成員都是私有的。例如在下面的類(lèi)中,width 是一個(gè)私有成員,這意味著,如果您沒(méi)有使用任何訪問(wèn)修飾符,類(lèi)的成員將被假定為私有成員:
class Box
{
double width;
public:
double length;
void setWidth( double wid );
double getWidth( void );
};
實(shí)際操作中,我們一般會(huì)在私有區(qū)域定義數(shù)據(jù),在公有區(qū)域定義相關(guān)的函數(shù),以便在類(lèi)的外部也可以調(diào)用這些函數(shù),如下所示:
#include <iostream>
using namespace std;
class Box
{
public:
double length;
void setWidth( double wid );
double getWidth( void );
private:
double width;
};
// 成員函數(shù)定義
double Box::getWidth(void)
{
return width ;
}
void Box::setWidth( double wid )
{
width = wid;
}
// 程序的主函數(shù)
int main( )
{
Box box;
// 不使用成員函數(shù)設(shè)置長(zhǎng)度
box.length = 10.0; // OK: 因?yàn)?length 是公有的
cout << "Length of box : " << box.length <<endl;
// 不使用成員函數(shù)設(shè)置寬度
// box.width = 10.0; // Error: 因?yàn)?width 是私有的
box.setWidth(10.0); // 使用成員函數(shù)設(shè)置寬度
cout << "Width of box : " << box.getWidth() <<endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Length of box : 10
Width of box : 10
保護(hù)(protected)成員
保護(hù)成員變量或函數(shù)與私有成員十分相似,但有一點(diǎn)不同,保護(hù)成員在派生類(lèi)(即子類(lèi))中是可訪問(wèn)的。
在下一個(gè)章節(jié)中,您將學(xué)習(xí)到派生類(lèi)和繼承的知識(shí)。現(xiàn)在您可以看到下面的實(shí)例中,我們從父類(lèi) Box 派生了一個(gè)子類(lèi) smallBox。
下面的實(shí)例與前面的實(shí)例類(lèi)似,在這里 width 成員可被派生類(lèi) smallBox 的任何成員函數(shù)訪問(wèn)。
#include <iostream>
using namespace std;
class Box
{
protected:
double width;
};
class SmallBox:Box // SmallBox 是派生類(lèi)
{
public:
void setSmallWidth( double wid );
double getSmallWidth( void );
};
// 子類(lèi)的成員函數(shù)
double SmallBox::getSmallWidth(void)
{
return width ;
}
void SmallBox::setSmallWidth( double wid )
{
width = wid;
}
// 程序的主函數(shù)
int main( )
{
SmallBox box;
// 使用成員函數(shù)設(shè)置寬度
box.setSmallWidth(5.0);
cout << "Width of box : "<< box.getSmallWidth() << endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Width of box : 5
繼承中的特點(diǎn)
有public, protected, private三種繼承方式,它們相應(yīng)地改變了基類(lèi)成員的訪問(wèn)屬性。
public 繼承:基類(lèi) public 成員,protected 成員,private 成員的訪問(wèn)屬性在派生類(lèi)中分別變成:public, protected, private
protected 繼承:基類(lèi) public 成員,protected 成員,private 成員的訪問(wèn)屬性在派生類(lèi)中分別變成:protected, protected, private
private 繼承:基類(lèi) public 成員,protected 成員,private 成員的訪問(wèn)屬性在派生類(lèi)中分別變成:private, private, private
但無(wú)論哪種繼承方式,上面兩點(diǎn)都沒(méi)有改變:
private 成員只能被本類(lèi)成員(類(lèi)內(nèi))和友元訪問(wèn),不能被派生類(lèi)訪問(wèn);
protected 成員可以被派生類(lèi)訪問(wèn)。
構(gòu)造函數(shù)和析構(gòu)函數(shù)
類(lèi)的構(gòu)造函數(shù)是類(lèi)的一種特殊的成員函數(shù),它會(huì)在每次創(chuàng)建類(lèi)的新對(duì)象時(shí)執(zhí)行。
構(gòu)造函數(shù)的名稱(chēng)與類(lèi)的名稱(chēng)是完全相同的,并且不會(huì)返回任何類(lèi)型,也不會(huì)返回 void。構(gòu)造函數(shù)可用于為某些成員變量設(shè)置初始值。
下面的實(shí)例有助于更好地理解構(gòu)造函數(shù)的概念:
#include <iostream>
using namespace std;
class Line
{
public:
void setLength( double len );
double getLength( void );
Line(); // 這是構(gòu)造函數(shù)
private:
double length;
};
// 成員函數(shù)定義,包括構(gòu)造函數(shù)
Line::Line(void)
{
cout << "Object is being created" << endl;
}
void Line::setLength( double len )
{
length = len;
}
double Line::getLength( void )
{
return length;
}
// 程序的主函數(shù)
int main( )
{
Line line;
// 設(shè)置長(zhǎng)度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Object is being created
Length of line : 6
帶參數(shù)的構(gòu)造函數(shù)
默認(rèn)的構(gòu)造函數(shù)沒(méi)有任何參數(shù),但如果需要,構(gòu)造函數(shù)也可以帶有參數(shù)。這樣在創(chuàng)建對(duì)象時(shí)就會(huì)給對(duì)象賦初始值,如下面的例子所示:
#include <iostream>
using namespace std;
class Line
{
public:
void setLength( double len );
double getLength( void );
Line(double len); // 這是構(gòu)造函數(shù)
private:
double length;
};
// 成員函數(shù)定義,包括構(gòu)造函數(shù)
Line::Line( double len)
{
cout << "Object is being created, length = " << len << endl;
length = len;
}
void Line::setLength( double len )
{
length = len;
}
double Line::getLength( void )
{
return length;
}
// 程序的主函數(shù)
int main( )
{
Line line(10.0);
// 獲取默認(rèn)設(shè)置的長(zhǎng)度
cout << "Length of line : " << line.getLength() <<endl;
// 再次設(shè)置長(zhǎng)度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Object is being created, length = 10
Length of line : 10
Length of line : 6
使用初始化列表來(lái)初始化字段
使用初始化列表來(lái)初始化字段:
Line::Line( double len): length(len)
{
cout << "Object is being created, length = " << len << endl;
}
上面的語(yǔ)法等同于如下語(yǔ)法:
Line::Line( double len)
{
cout << "Object is being created, length = " << len << endl;
length = len;
}
假設(shè)有一個(gè)類(lèi) C,具有多個(gè)字段 X、Y、Z 等需要進(jìn)行初始化,同理地,您可以使用上面的語(yǔ)法,只需要在不同的字段使用逗號(hào)進(jìn)行分隔,如下所示:
C::C( double a, double b, double c): X(a), Y(b), Z(c)
{
....
}
類(lèi)的析構(gòu)函數(shù)
類(lèi)的析構(gòu)函數(shù)是類(lèi)的一種特殊的成員函數(shù),它會(huì)在每次刪除所創(chuàng)建的對(duì)象時(shí)執(zhí)行。
析構(gòu)函數(shù)的名稱(chēng)與類(lèi)的名稱(chēng)是完全相同的,只是在前面加了個(gè)波浪號(hào)(~)作為前綴,它不會(huì)返回任何值,也不能帶有任何參數(shù)。析構(gòu)函數(shù)有助于在跳出程序(比如關(guān)閉文件、釋放內(nèi)存等)前釋放資源。
下面的實(shí)例有助于更好地理解析構(gòu)函數(shù)的概念:
#include <iostream>
using namespace std;
class Line
{
public:
void setLength( double len );
double getLength( void );
Line(); // 這是構(gòu)造函數(shù)聲明
~Line(); // 這是析構(gòu)函數(shù)聲明
private:
double length;
};
// 成員函數(shù)定義,包括構(gòu)造函數(shù)
Line::Line(void)
{
cout << "Object is being created" << endl;
}
Line::~Line(void)
{
cout << "Object is being deleted" << endl;
}
void Line::setLength( double len )
{
length = len;
}
double Line::getLength( void )
{
return length;
}
// 程序的主函數(shù)
int main( )
{
Line line;
// 設(shè)置長(zhǎng)度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Object is being created
Length of line : 6
Object is being deleted
C++拷貝構(gòu)造函數(shù)
拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),它在創(chuàng)建對(duì)象時(shí),是使用同一類(lèi)中之前創(chuàng)建的對(duì)象來(lái)初始化新創(chuàng)建的對(duì)象??截悩?gòu)造函數(shù)通常用于:
通過(guò)使用另一個(gè)同類(lèi)型的對(duì)象來(lái)初始化新創(chuàng)建的對(duì)象。
復(fù)制對(duì)象把它作為參數(shù)傳遞給函數(shù)。
復(fù)制對(duì)象,并從函數(shù)返回這個(gè)對(duì)象。
如果在類(lèi)中沒(méi)有定義拷貝構(gòu)造函數(shù),編譯器會(huì)自行定義一個(gè)。如果類(lèi)帶有指針變量,并有動(dòng)態(tài)內(nèi)存分配,則它必須有一個(gè)拷貝構(gòu)造函數(shù)??截悩?gòu)造函數(shù)的最常見(jiàn)形式如下:
classname (const classname &obj) {
// 構(gòu)造函數(shù)的主體
}
在這里,obj 是一個(gè)對(duì)象引用,該對(duì)象是用于初始化另一個(gè)對(duì)象的。
#include <iostream>
using namespace std;
class Line
{
public:
int getLength( void );
Line( int len ); // 簡(jiǎn)單的構(gòu)造函數(shù)
Line( const Line &obj); // 拷貝構(gòu)造函數(shù)
~Line(); // 析構(gòu)函數(shù)
private:
int *ptr;
};
// 成員函數(shù)定義,包括構(gòu)造函數(shù)
Line::Line(int len)
{
cout << "調(diào)用構(gòu)造函數(shù)" << endl;
// 為指針?lè)峙鋬?nèi)存
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj)
{
cout << "調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷貝值
}
Line::~Line(void)
{
cout << "釋放內(nèi)存" << endl;
delete ptr;
}
int Line::getLength( void )
{
return *ptr;
}
void display(Line obj)
{
cout << "line 大小 : " << obj.getLength() <<endl;
}
// 程序的主函數(shù)
int main( )
{
Line line(10);
display(line);
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
調(diào)用構(gòu)造函數(shù)
調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存
line 大小 : 10
釋放內(nèi)存
釋放內(nèi)存
下面的實(shí)例對(duì)上面的實(shí)例稍作修改,通過(guò)使用已有的同類(lèi)型的對(duì)象來(lái)初始化新創(chuàng)建的對(duì)象:
#include <iostream>
using namespace std;
class Line
{
public:
int getLength( void );
Line( int len ); // 簡(jiǎn)單的構(gòu)造函數(shù)
Line( const Line &obj); // 拷貝構(gòu)造函數(shù)
~Line(); // 析構(gòu)函數(shù)
private:
int *ptr;
};
// 成員函數(shù)定義,包括構(gòu)造函數(shù)
Line::Line(int len)
{
cout << "調(diào)用構(gòu)造函數(shù)" << endl;
// 為指針?lè)峙鋬?nèi)存
ptr = new int;
*ptr = len;
}
Line::Line(const Line &obj)
{
cout << "調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷貝值
}
Line::~Line(void)
{
cout << "釋放內(nèi)存" << endl;
delete ptr;
}
int Line::getLength( void )
{
return *ptr;
}
void display(Line obj)
{
cout << "line 大小 : " << obj.getLength() <<endl;
}
// 程序的主函數(shù)
int main( )
{
Line line1(10);
Line line2 = line1; // 這里也調(diào)用了拷貝構(gòu)造函數(shù)
display(line1);
display(line2);
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
調(diào)用構(gòu)造函數(shù)
調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存
調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存
line 大小 : 10
釋放內(nèi)存
調(diào)用拷貝構(gòu)造函數(shù)并為指針 ptr 分配內(nèi)存
line 大小 : 10
釋放內(nèi)存
釋放內(nèi)存
釋放內(nèi)存
C++友元函數(shù)
類(lèi)的友元函數(shù)是定義在類(lèi)外部,但有權(quán)訪問(wèn)類(lèi)的所有私有(private)成員和保護(hù)(protected)成員。盡管友元函數(shù)的原型有在類(lèi)的定義中出現(xiàn)過(guò),但是友元函數(shù)并不是成員函數(shù)。
友元可以是一個(gè)函數(shù),該函數(shù)被稱(chēng)為友元函數(shù);友元也可以是一個(gè)類(lèi),該類(lèi)被稱(chēng)為友元類(lèi),在這種情況下,整個(gè)類(lèi)及其所有成員都是友元。
如果要聲明函數(shù)為一個(gè)類(lèi)的友元,需要在類(lèi)定義中該函數(shù)原型前使用關(guān)鍵字 friend,如下所示:
class Box
{
double width;
public:
double length;
friend void printWidth( Box box );
void setWidth( double wid );
};
聲明類(lèi) ClassTwo 的所有成員函數(shù)作為類(lèi) ClassOne 的友元,需要在類(lèi) ClassOne 的定義中放置如下聲明:
friend class ClassTwo;
請(qǐng)看下面的程序:
#include <iostream>
using namespace std;
class Box
{
double width;
public:
friend void printWidth( Box box );
void setWidth( double wid );
};
// 成員函數(shù)定義
void Box::setWidth( double wid )
{
width = wid;
}
// 請(qǐng)注意:printWidth() 不是任何類(lèi)的成員函數(shù)
void printWidth( Box box )
{
/* 因?yàn)?printWidth() 是 Box 的友元,它可以直接訪問(wèn)該類(lèi)的任何成員 */
cout << "Width of box : " << box.width <<endl;
}
// 程序的主函數(shù)
int main( )
{
Box box;
// 使用成員函數(shù)設(shè)置寬度
box.setWidth(10.0);
// 使用友元函數(shù)輸出寬度
printWidth( box );
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Width of box : 10
內(nèi)聯(lián)函數(shù)
C++ 內(nèi)聯(lián)函數(shù)是通常與類(lèi)一起使用。如果一個(gè)函數(shù)是內(nèi)聯(lián)的,那么在編譯時(shí),編譯器會(huì)把該函數(shù)的代碼副本放置在每個(gè)調(diào)用該函數(shù)的地方。
對(duì)內(nèi)聯(lián)函數(shù)進(jìn)行任何修改,都需要重新編譯函數(shù)的所有客戶(hù)端,因?yàn)榫幾g器需要重新更換一次所有的代碼,否則將會(huì)繼續(xù)使用舊的函數(shù)。
如果想把一個(gè)函數(shù)定義為內(nèi)聯(lián)函數(shù),則需要在函數(shù)名前面放置關(guān)鍵字 inline,在調(diào)用函數(shù)之前需要對(duì)函數(shù)進(jìn)行定義。如果已定義的函數(shù)多于一行,編譯器會(huì)忽略 inline 限定符。
在類(lèi)定義中的定義的函數(shù)都是內(nèi)聯(lián)函數(shù),即使沒(méi)有使用 inline 說(shuō)明符。
下面是一個(gè)實(shí)例,使用內(nèi)聯(lián)函數(shù)來(lái)返回兩個(gè)數(shù)中的最大值:
#include <iostream>
using namespace std;
inline int Max(int x, int y)
{
return (x > y)? x : y;
}
// 程序的主函數(shù)
int main( )
{
cout << "Max (20,10): " << Max(20,10) << endl;
cout << "Max (0,200): " << Max(0,200) << endl;
cout << "Max (100,1010): " << Max(100,1010) << endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Max (20,10): 20
Max (0,200): 200
Max (100,1010): 1010
C++中的this指針
在 C++ 中,每一個(gè)對(duì)象都能通過(guò) this 指針來(lái)訪問(wèn)自己的地址。this 指針是所有成員函數(shù)的隱含參數(shù)。因此,在成員函數(shù)內(nèi)部,它可以用來(lái)指向調(diào)用對(duì)象。
友元函數(shù)沒(méi)有 this 指針,因?yàn)橛言皇穷?lèi)的成員。只有成員函數(shù)才有 this 指針。
下面的實(shí)例有助于更好地理解 this 指針的概念:
#include <iostream>
using namespace std;
class Box
{
public:
// 構(gòu)造函數(shù)定義
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume()
{
return length * breadth * height;
}
int compare(Box box)
{
return this->Volume() > box.Volume();
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
int main(void)
{
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
if(Box1.compare(Box2))
{
cout << "Box2 is smaller than Box1" <<endl;
}
else
{
cout << "Box2 is equal to or larger than Box1" <<endl;
}
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Constructor called.
Constructor called.
Box2 is equal to or larger than Box1
C++中指向類(lèi)的指針
一個(gè)指向 C++ 類(lèi)的指針與指向結(jié)構(gòu)的指針類(lèi)似,訪問(wèn)指向類(lèi)的指針的成員,需要使用成員訪問(wèn)運(yùn)算符 ->,就像訪問(wèn)指向結(jié)構(gòu)的指針一樣。與所有的指針一樣,您必須在使用指針之前,對(duì)指針進(jìn)行初始化。
下面的實(shí)例有助于更好地理解指向類(lèi)的指針的概念:
#include <iostream>
using namespace std;
class Box
{
public:
// 構(gòu)造函數(shù)定義
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume()
{
return length * breadth * height;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
int main(void)
{
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
Box *ptrBox; // Declare pointer to a class.
// 保存第一個(gè)對(duì)象的地址
ptrBox = &Box1;
// 現(xiàn)在嘗試使用成員訪問(wèn)運(yùn)算符來(lái)訪問(wèn)成員
cout << "Volume of Box1: " << ptrBox->Volume() << endl;
// 保存第二個(gè)對(duì)象的地址
ptrBox = &Box2;
// 現(xiàn)在嘗試使用成員訪問(wèn)運(yùn)算符來(lái)訪問(wèn)成員
cout << "Volume of Box2: " << ptrBox->Volume() << endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Constructor called.
Constructor called.
Volume of Box1: 5.94
Volume of Box2: 102
C++類(lèi)的靜態(tài)成員
我們可以使用 static 關(guān)鍵字來(lái)把類(lèi)成員定義為靜態(tài)的。當(dāng)我們聲明類(lèi)的成員為靜態(tài)時(shí),這意味著無(wú)論創(chuàng)建多少個(gè)類(lèi)的對(duì)象,靜態(tài)成員都只有一個(gè)副本。
靜態(tài)成員在類(lèi)的所有對(duì)象中是共享的。如果不存在其他的初始化語(yǔ)句,在創(chuàng)建第一個(gè)對(duì)象時(shí),所有的靜態(tài)數(shù)據(jù)都會(huì)被初始化為零。我們不能把靜態(tài)成員的初始化放置在類(lèi)的定義中,但是可以在類(lèi)的外部通過(guò)使用范圍解析運(yùn)算符 :: 來(lái)重新聲明靜態(tài)變量從而對(duì)它進(jìn)行初始化,如下面的實(shí)例所示。
下面的實(shí)例有助于更好地理解靜成員態(tài)數(shù)據(jù)的概念:
#include <iostream>
using namespace std;
class Box
{
public:
static int objectCount;
// 構(gòu)造函數(shù)定義
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
// 每次創(chuàng)建對(duì)象時(shí)增加 1
objectCount++;
}
double Volume()
{
return length * breadth * height;
}
private:
double length; // 長(zhǎng)度
double breadth; // 寬度
double height; // 高度
};
// 初始化類(lèi) Box 的靜態(tài)成員
int Box::objectCount = 0;
int main(void)
{
Box Box1(3.3, 1.2, 1.5); // 聲明 box1
Box Box2(8.5, 6.0, 2.0); // 聲明 box2
// 輸出對(duì)象的總數(shù)
cout << "Total objects: " << Box::objectCount << endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Constructor called.
Constructor called.
Total objects: 2
靜態(tài)成員函數(shù)
如果把函數(shù)成員聲明為靜態(tài)的,就可以把函數(shù)與類(lèi)的任何特定對(duì)象獨(dú)立開(kāi)來(lái)。靜態(tài)成員函數(shù)即使在類(lèi)對(duì)象不存在的情況下也能被調(diào)用,靜態(tài)函數(shù)只要使用類(lèi)名加范圍解析運(yùn)算符 :: 就可以訪問(wèn)。
靜態(tài)成員函數(shù)只能訪問(wèn)靜態(tài)成員數(shù)據(jù)、其他靜態(tài)成員函數(shù)和類(lèi)外部的其他函數(shù)。
靜態(tài)成員函數(shù)有一個(gè)類(lèi)范圍,他們不能訪問(wèn)類(lèi)的 this 指針。您可以使用靜態(tài)成員函數(shù)來(lái)判斷類(lèi)的某些對(duì)象是否已被創(chuàng)建。
靜態(tài)成員函數(shù)與普通成員函數(shù)的區(qū)別:
靜態(tài)成員函數(shù)沒(méi)有 this 指針,只能訪問(wèn)靜態(tài)成員(包括靜態(tài)成員變量和靜態(tài)成員函數(shù))。普通成員函數(shù)有 this 指針,可以訪問(wèn)類(lèi)中的任意成員;而靜態(tài)成員函數(shù)沒(méi)有 this 指針。
下面的實(shí)例有助于更好地理解靜態(tài)成員函數(shù)的概念:
#include <iostream>
using namespace std;
class Box
{
public:
static int objectCount;
// 構(gòu)造函數(shù)定義
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
// 每次創(chuàng)建對(duì)象時(shí)增加 1
objectCount++;
}
double Volume()
{
return length * breadth * height;
}
static int getCount()
{
return objectCount;
}
private:
double length; // 長(zhǎng)度
double breadth; // 寬度
double height; // 高度
};
// 初始化類(lèi) Box 的靜態(tài)成員
int Box::objectCount = 0;
int main(void)
{
// 在創(chuàng)建對(duì)象之前輸出對(duì)象的總數(shù)
cout << "Inital Stage Count: " << Box::getCount() << endl;
Box Box1(3.3, 1.2, 1.5); // 聲明 box1
Box Box2(8.5, 6.0, 2.0); // 聲明 box2
// 在創(chuàng)建對(duì)象之后輸出對(duì)象的總數(shù)
cout << "Final Stage Count: " << Box::getCount() << endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2
C++繼承
面向?qū)ο蟪绦蛟O(shè)計(jì)中最重要的一個(gè)概念是繼承。繼承允許我們依據(jù)另一個(gè)類(lèi)來(lái)定義一個(gè)類(lèi),這使得創(chuàng)建和維護(hù)一個(gè)應(yīng)用程序變得更容易。這樣做,也達(dá)到了重用代碼功能和提高執(zhí)行時(shí)間的效果。
當(dāng)創(chuàng)建一個(gè)類(lèi)時(shí),您不需要重新編寫(xiě)新的數(shù)據(jù)成員和成員函數(shù),只需指定新建的類(lèi)繼承了一個(gè)已有的類(lèi)的成員即可。這個(gè)已有的類(lèi)稱(chēng)為基類(lèi),新建的類(lèi)稱(chēng)為派生類(lèi)。
繼承代表了 is a 關(guān)系。例如,哺乳動(dòng)物是動(dòng)物,狗是哺乳動(dòng)物,因此,狗是動(dòng)物,等等。
基類(lèi)和派生類(lèi)
一個(gè)類(lèi)可以派生自多個(gè)類(lèi),這意味著,它可以從多個(gè)基類(lèi)繼承數(shù)據(jù)和函數(shù)。定義一個(gè)派生類(lèi),我們使用一個(gè)類(lèi)派生列表來(lái)指定基類(lèi)。類(lèi)派生列表以一個(gè)或多個(gè)基類(lèi)命名,形式如下:
class derived-class: access-specifier base-class
其中,訪問(wèn)修飾符 access-specifier 是 public、protected 或 private 其中的一個(gè),base-class 是之前定義過(guò)的某個(gè)類(lèi)的名稱(chēng)。如果未使用訪問(wèn)修飾符 access-specifier,則默認(rèn)為 private。
假設(shè)有一個(gè)基類(lèi) Shape,Rectangle 是它的派生類(lèi),如下所示:
#include <iostream>
using namespace std;
// 基類(lèi)
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 派生類(lèi)
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
// 輸出對(duì)象的面積
cout << "Total area: " << Rect.getArea() << endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Total area: 35
訪問(wèn)控制和繼承
派生類(lèi)可以訪問(wèn)基類(lèi)中所有的非私有成員。因此基類(lèi)成員如果不想被派生類(lèi)的成員函數(shù)訪問(wèn),則應(yīng)在基類(lèi)中聲明為 private。
我們可以根據(jù)訪問(wèn)權(quán)限總結(jié)出不同的訪問(wèn)類(lèi)型,如下所示:
| 訪問(wèn) | public | protected | private |
|---|---|---|---|
| 同一個(gè)類(lèi) | yes | yes | yes |
| 派生類(lèi) | yes | yes | no |
| 外部的類(lèi) | yes | no | no |
一個(gè)派生類(lèi)繼承了所有的基類(lèi)方法,但下列情況除外:
- 基類(lèi)的構(gòu)造函數(shù)、析構(gòu)函數(shù)和拷貝構(gòu)造函數(shù)。
- 基類(lèi)的重載運(yùn)算符。
- 基類(lèi)的友元函數(shù)。
繼承類(lèi)型
當(dāng)一個(gè)類(lèi)派生自基類(lèi),該基類(lèi)可以被繼承為 public、protected 或 private 幾種類(lèi)型。繼承類(lèi)型是通過(guò)上面講解的訪問(wèn)修飾符 access-specifier 來(lái)指定的。
我們幾乎不使用 protected 或 private 繼承,通常使用 public 繼承。當(dāng)使用不同類(lèi)型的繼承時(shí),遵循以下幾個(gè)規(guī)則:
- 公有繼承(public):當(dāng)一個(gè)類(lèi)派生自公有基類(lèi)時(shí),基類(lèi)的公有成員也是派生類(lèi)的公有成員,基類(lèi)的保護(hù)成員也是派生類(lèi)的保護(hù)成員,基類(lèi)的私有成員不能直接被派生類(lèi)訪問(wèn),但是可以通過(guò)調(diào)用基類(lèi)的公有和保護(hù)成員來(lái)訪問(wèn)。
- 保護(hù)繼承(protected): 當(dāng)一個(gè)類(lèi)派生自保護(hù)基類(lèi)時(shí),基類(lèi)的公有和保護(hù)成員將成為派生類(lèi)的保護(hù)成員。
- 私有繼承(private):當(dāng)一個(gè)類(lèi)派生自私有基類(lèi)時(shí),基類(lèi)的公有和保護(hù)成員將成為派生類(lèi)的私有成員。
多繼承
多繼承即一個(gè)子類(lèi)可以有多個(gè)父類(lèi),它繼承了多個(gè)父類(lèi)的特性。
C++ 類(lèi)可以從多個(gè)類(lèi)繼承成員,語(yǔ)法如下:
class <派生類(lèi)名>:<繼承方式1><基類(lèi)名1>,<繼承方式2><基類(lèi)名2>,…
{
<派生類(lèi)類(lèi)體>
};
其中,訪問(wèn)修飾符繼承方式是 public、protected 或 private 其中的一個(gè),用來(lái)修飾每個(gè)基類(lèi),各個(gè)基類(lèi)之間用逗號(hào)分隔,如上所示?,F(xiàn)在讓我們一起看看下面的實(shí)例:
#include <iostream>
using namespace std;
// 基類(lèi) Shape
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 基類(lèi) PaintCost
class PaintCost
{
public:
int getCost(int area)
{
return area * 70;
}
};
// 派生類(lèi)
class Rectangle: public Shape, public PaintCost
{
public:
int getArea()
{
return (width * height);
}
};
int main(void)
{
Rectangle Rect;
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// 輸出對(duì)象的面積
cout << "Total area: " << Rect.getArea() << endl;
// 輸出總花費(fèi)
cout << "Total paint cost: $" << Rect.getCost(area) << endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Total area: 35
Total paint cost: $2450
C++重載運(yùn)算符和重載函數(shù)
C++ 允許在同一作用域中的某個(gè)函數(shù)和運(yùn)算符指定多個(gè)定義,分別稱(chēng)為函數(shù)重載和運(yùn)算符重載。
重載聲明是指一個(gè)與之前已經(jīng)在該作用域內(nèi)聲明過(guò)的函數(shù)或方法具有相同名稱(chēng)的聲明,但是它們的參數(shù)列表和定義(實(shí)現(xiàn))不相同。
當(dāng)您調(diào)用一個(gè)重載函數(shù)或重載運(yùn)算符時(shí),編譯器通過(guò)把您所使用的參數(shù)類(lèi)型與定義中的參數(shù)類(lèi)型進(jìn)行比較,決定選用最合適的定義。選擇最合適的重載函數(shù)或重載運(yùn)算符的過(guò)程,稱(chēng)為重載決策。
C++中的函數(shù)重載
在同一個(gè)作用域內(nèi),可以聲明幾個(gè)功能類(lèi)似的同名函數(shù),但是這些同名函數(shù)的形式參數(shù)(指參數(shù)的個(gè)數(shù)、類(lèi)型或者順序)必須不同。您不能僅通過(guò)返回類(lèi)型的不同來(lái)重載函數(shù)。
下面的實(shí)例中,同名函數(shù) print() 被用于輸出不同的數(shù)據(jù)類(lèi)型:
#include <iostream>
using namespace std;
class printData
{
public:
void print(int i) {
cout << "整數(shù)為: " << i << endl;
}
void print(double f) {
cout << "浮點(diǎn)數(shù)為: " << f << endl;
}
void print(string c) {
cout << "字符串為: " << c << endl;
}
};
int main(void)
{
printData pd;
// 輸出整數(shù)
pd.print(5);
// 輸出浮點(diǎn)數(shù)
pd.print(500.263);
// 輸出字符串
pd.print("Hello C++");
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
整數(shù)為: 5
浮點(diǎn)數(shù)為: 500.263
字符串為: Hello C++
C++中的運(yùn)算符重載
您可以重定義或重載大部分 C++ 內(nèi)置的運(yùn)算符。這樣,您就能使用自定義類(lèi)型的運(yùn)算符。
重載的運(yùn)算符是帶有特殊名稱(chēng)的函數(shù),函數(shù)名是由關(guān)鍵字 operator 和其后要重載的運(yùn)算符符號(hào)構(gòu)成的。與其他函數(shù)一樣,重載運(yùn)算符有一個(gè)返回類(lèi)型和一個(gè)參數(shù)列表。
Box operator+(const Box&);
聲明加法運(yùn)算符用于把兩個(gè) Box 對(duì)象相加,返回最終的 Box 對(duì)象。大多數(shù)的重載運(yùn)算符可被定義為普通的非成員函數(shù)或者被定義為類(lèi)成員函數(shù)。如果我們定義上面的函數(shù)為類(lèi)的非成員函數(shù),那么我們需要為每次操作傳遞兩個(gè)參數(shù),如下所示:
Box operator+(const Box&, const Box&);
下面的實(shí)例使用成員函數(shù)演示了運(yùn)算符重載的概念。在這里,對(duì)象作為參數(shù)進(jìn)行傳遞,對(duì)象的屬性使用 this 運(yùn)算符進(jìn)行訪問(wèn),如下所示:
#include <iostream>
using namespace std;
class Box
{
public:
double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}
void setBreadth( double bre )
{
breadth = bre;
}
void setHeight( double hei )
{
height = hei;
}
// 重載 + 運(yùn)算符,用于把兩個(gè) Box 對(duì)象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // 長(zhǎng)度
double breadth; // 寬度
double height; // 高度
};
// 程序的主函數(shù)
int main( )
{
Box Box1; // 聲明 Box1,類(lèi)型為 Box
Box Box2; // 聲明 Box2,類(lèi)型為 Box
Box Box3; // 聲明 Box3,類(lèi)型為 Box
double volume = 0.0; // 把體積存儲(chǔ)在該變量中
// Box1 詳述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 詳述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// Box1 的體積
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;
// Box2 的體積
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;
// 把兩個(gè)對(duì)象相加,得到 Box3
Box3 = Box1 + Box2;
// Box3 的體積
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400
可重載運(yùn)算符和不可重載運(yùn)算符
下面是可重載的運(yùn)算符列表:
- 雙目算術(shù)運(yùn)算符 + (加),-(減),*(乘),/(除),% (取模)
- 關(guān)系運(yùn)算符 ==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于)
- 邏輯運(yùn)算符 ||(邏輯或),&&(邏輯與),!(邏輯非)
- 單目運(yùn)算符 + (正),-(負(fù)),*(指針),&(取地址)
- 自增自減運(yùn)算符 ++(自增),--(自減)
- 位運(yùn)算符 | (按位或),& (按位與),~(按位取反),^(按位異或),,<< (左移),>>(右移)
- 賦值運(yùn)算符 =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=
- 空間申請(qǐng)與釋放 new, delete, new[ ] , delete[]
- 其他運(yùn)算符 ()(函數(shù)調(diào)用),->(成員訪問(wèn)),->*(成員指針訪問(wèn)),,(逗號(hào)),
下面是不可重載的運(yùn)算符列表:
- .:成員訪問(wèn)運(yùn)算符
- ., ->:成員指針訪問(wèn)運(yùn)算符
- :::域運(yùn)算符
- sizeof:長(zhǎng)度運(yùn)算符
- ?::條件運(yùn)算符
-
: 預(yù)處理符號(hào)
運(yùn)算符重載實(shí)例
一元運(yùn)算符重載
一元運(yùn)算符只對(duì)一個(gè)操作數(shù)進(jìn)行操作,下面是一元運(yùn)算符的實(shí)例:
- 遞增運(yùn)算符( ++ )和遞減運(yùn)算符( -- )
- 一元減運(yùn)算符,即負(fù)號(hào)( - )
- 邏輯非運(yùn)算符( ! )
一元運(yùn)算符通常出現(xiàn)在它們所操作的對(duì)象的左邊,比如 !obj、-obj 和 ++obj,但有時(shí)它們也可以作為后綴,比如 obj++ 或 obj--。
下面的實(shí)例演示了如何重載一元減運(yùn)算符( - )。
#include <iostream>
using namespace std;
class Distance
{
private:
int feet; // 0 到無(wú)窮
int inches; // 0 到 12
public:
// 所需的構(gòu)造函數(shù)
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
// 顯示距離的方法
void displayDistance()
{
cout << "F: " << feet << " I:" << inches <<endl;
}
// 重載負(fù)運(yùn)算符( - )
Distance operator- ()
{
feet = -feet;
inches = -inches;
return Distance(feet, inches);
}
};
int main()
{
Distance D1(11, 10), D2(-5, 11);
-D1; // 取相反數(shù)
D1.displayDistance(); // 距離 D1
-D2; // 取相反數(shù)
D2.displayDistance(); // 距離 D2
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
F: -11 I:-10
F: 5 I:-11
希望上面的實(shí)例能夠幫您更好地理解一元運(yùn)算符重載的概念,類(lèi)似地,您可以嘗試重載邏輯非運(yùn)算符( ! )。
二元運(yùn)算符重載
二元運(yùn)算符需要兩個(gè)參數(shù),下面是二元運(yùn)算符的實(shí)例。我們平常使用的加運(yùn)算符( + )、減運(yùn)算符( - )、乘運(yùn)算符( * )和除運(yùn)算符( / )都屬于二元運(yùn)算符。就像加(+)運(yùn)算符。
下面的實(shí)例演示了如何重載加運(yùn)算符( + )。類(lèi)似地,您也可以嘗試重載減運(yùn)算符( - )和除運(yùn)算符( / )。
#include <iostream>
using namespace std;
class Box
{
double length; // 長(zhǎng)度
double breadth; // 寬度
double height; // 高度
public:
double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}
void setBreadth( double bre )
{
breadth = bre;
}
void setHeight( double hei )
{
height = hei;
}
// 重載 + 運(yùn)算符,用于把兩個(gè) Box 對(duì)象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
};
// 程序的主函數(shù)
int main( )
{
Box Box1; // 聲明 Box1,類(lèi)型為 Box
Box Box2; // 聲明 Box2,類(lèi)型為 Box
Box Box3; // 聲明 Box3,類(lèi)型為 Box
double volume = 0.0; // 把體積存儲(chǔ)在該變量中
// Box1 詳述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 詳述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// Box1 的體積
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;
// Box2 的體積
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;
// 把兩個(gè)對(duì)象相加,得到 Box3
Box3 = Box1 + Box2;
// Box3 的體積
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400
關(guān)系運(yùn)算符重載
C++ 語(yǔ)言支持各種關(guān)系運(yùn)算符( < 、 > 、 <= 、 >= 、 == 等等),它們可用于比較 C++ 內(nèi)置的數(shù)據(jù)類(lèi)型。
您可以重載任何一個(gè)關(guān)系運(yùn)算符,重載后的關(guān)系運(yùn)算符可用于比較類(lèi)的對(duì)象。
下面的實(shí)例演示了如何重載 < 運(yùn)算符,類(lèi)似地,您也可以嘗試重載其他的關(guān)系運(yùn)算符。
#include <iostream>
using namespace std;
class Distance
{
private:
int feet; // 0 到無(wú)窮
int inches; // 0 到 12
public:
// 所需的構(gòu)造函數(shù)
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
// 顯示距離的方法
void displayDistance()
{
cout << "F: " << feet << " I:" << inches <<endl;
}
// 重載負(fù)運(yùn)算符( - )
Distance operator- ()
{
feet = -feet;
inches = -inches;
return Distance(feet, inches);
}
// 重載小于運(yùn)算符( < )
bool operator <(const Distance& d)
{
if(feet < d.feet)
{
return true;
}
if(feet == d.feet && inches < d.inches)
{
return true;
}
return false;
}
};
int main()
{
Distance D1(11, 10), D2(5, 11);
if( D1 < D2 )
{
cout << "D1 is less than D2 " << endl;
}
else
{
cout << "D2 is less than D1 " << endl;
}
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
D2 is less than D1
輸入和輸出運(yùn)算符重載
C++ 能夠使用流提取運(yùn)算符 >> 和流插入運(yùn)算符 << 來(lái)輸入和輸出內(nèi)置的數(shù)據(jù)類(lèi)型。您可以重載流提取運(yùn)算符和流插入運(yùn)算符來(lái)操作對(duì)象等用戶(hù)自定義的數(shù)據(jù)類(lèi)型。
在這里,有一點(diǎn)很重要,我們需要把運(yùn)算符重載函數(shù)聲明為類(lèi)的友元函數(shù),這樣我們就能不用創(chuàng)建對(duì)象而直接調(diào)用函數(shù)。
下面的實(shí)例演示了如何重載提取運(yùn)算符 >> 和插入運(yùn)算符 <<。
#include <iostream>
using namespace std;
class Distance
{
private:
int feet; // 0 到無(wú)窮
int inches; // 0 到 12
public:
// 所需的構(gòu)造函數(shù)
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
friend ostream &operator<<( ostream &output,
const Distance &D )
{
output << "F : " << D.feet << " I : " << D.inches;
return output;
}
friend istream &operator>>( istream &input, Distance &D )
{
input >> D.feet >> D.inches;
return input;
}
};
int main()
{
Distance D1(11, 10), D2(5, 11), D3;
cout << "Enter the value of object : " << endl;
cin >> D3;
cout << "First Distance : " << D1 << endl;
cout << "Second Distance :" << D2 << endl;
cout << "Third Distance :" << D3 << endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
$./a.out
Enter the value of object :
70
10
First Distance : F : 11 I : 10
Second Distance :F : 5 I : 11
Third Distance :F : 70 I : 10
++和--運(yùn)算符重載
遞增運(yùn)算符( ++ )和遞減運(yùn)算符( -- )是 C++ 語(yǔ)言中兩個(gè)重要的一元運(yùn)算符。
下面的實(shí)例演示了如何重載遞增運(yùn)算符( ++ ),包括前綴和后綴兩種用法。類(lèi)似地,您也可以嘗試重載遞減運(yùn)算符( -- )。
#include <iostream>
using namespace std;
class Time
{
private:
int hours; // 0 到 23
int minutes; // 0 到 59
public:
// 所需的構(gòu)造函數(shù)
Time(){
hours = 0;
minutes = 0;
}
Time(int h, int m){
hours = h;
minutes = m;
}
// 顯示時(shí)間的方法
void displayTime()
{
cout << "H: " << hours << " M:" << minutes <<endl;
}
// 重載前綴遞增運(yùn)算符( ++ )
Time operator++ ()
{
++minutes; // 對(duì)象加 1
if(minutes >= 60)
{
++hours;
minutes -= 60;
}
return Time(hours, minutes);
}
// 重載后綴遞增運(yùn)算符( ++ )
Time operator++( int )
{
// 保存原始值
Time T(hours, minutes);
// 對(duì)象加 1
++minutes;
if(minutes >= 60)
{
++hours;
minutes -= 60;
}
// 返回舊的原始值
return T;
}
};
int main()
{
Time T1(11, 59), T2(10,40);
++T1; // T1 加 1
T1.displayTime(); // 顯示 T1
++T1; // T1 再加 1
T1.displayTime(); // 顯示 T1
T2++; // T2 加 1
T2.displayTime(); // 顯示 T2
T2++; // T2 再加 1
T2.displayTime(); // 顯示 T2
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
H: 12 M:0
H: 12 M:1
H: 10 M:41
H: 10 M:42
賦值運(yùn)算符重載
就像其他運(yùn)算符一樣,您可以重載賦值運(yùn)算符( = ),用于創(chuàng)建一個(gè)對(duì)象,比如拷貝構(gòu)造函數(shù)。
下面的實(shí)例演示了如何重載賦值運(yùn)算符。
#include <iostream>
using namespace std;
class Distance
{
private:
int feet; // 0 到無(wú)窮
int inches; // 0 到 12
public:
// 所需的構(gòu)造函數(shù)
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
void operator=(const Distance &D )
{
feet = D.feet;
inches = D.inches;
}
// 顯示距離的方法
void displayDistance()
{
cout << "F: " << feet << " I:" << inches << endl;
}
};
int main()
{
Distance D1(11, 10), D2(5, 11);
cout << "First Distance : ";
D1.displayDistance();
cout << "Second Distance :";
D2.displayDistance();
// 使用賦值運(yùn)算符
D1 = D2;
cout << "First Distance :";
D1.displayDistance();
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
First Distance : F: 11 I:10
Second Distance :F: 5 I:11
First Distance :F: 5 I:11
函數(shù)調(diào)用運(yùn)算符()重載
函數(shù)調(diào)用運(yùn)算符 () 可以被重載用于類(lèi)的對(duì)象。當(dāng)重載 () 時(shí),您不是創(chuàng)造了一種新的調(diào)用函數(shù)的方式,相反地,這是創(chuàng)建一個(gè)可以傳遞任意數(shù)目參數(shù)的運(yùn)算符函數(shù)。
下面的實(shí)例演示了如何重載函數(shù)調(diào)用運(yùn)算符 ()。
#include <iostream>
using namespace std;
class Distance
{
private:
int feet; // 0 到無(wú)窮
int inches; // 0 到 12
public:
// 所需的構(gòu)造函數(shù)
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
// 重載函數(shù)調(diào)用運(yùn)算符
Distance operator()(int a, int b, int c)
{
Distance D;
// 進(jìn)行隨機(jī)計(jì)算
D.feet = a + c + 10;
D.inches = b + c + 100 ;
return D;
}
// 顯示距離的方法
void displayDistance()
{
cout << "F: " << feet << " I:" << inches << endl;
}
};
int main()
{
Distance D1(11, 10), D2;
cout << "First Distance : ";
D1.displayDistance();
D2 = D1(10, 10, 10); // invoke operator()
cout << "Second Distance :";
D2.displayDistance();
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
First Distance : F: 11 I:10
Second Distance :F: 30 I:120
下標(biāo)運(yùn)算符[]重載
下標(biāo)操作符 [] 通常用于訪問(wèn)數(shù)組元素。重載該運(yùn)算符用于增強(qiáng)操作 C++ 數(shù)組的功能。
下面的實(shí)例演示了如何重載下標(biāo)運(yùn)算符 []。
#include <iostream>
using namespace std;
const int SIZE = 10;
class safearay
{
private:
int arr[SIZE];
public:
safearay()
{
register int i;
for(i = 0; i < SIZE; i++)
{
arr[i] = i;
}
}
int& operator[](int i)
{
if( i > SIZE )
{
cout << "索引超過(guò)最大值" <<endl;
// 返回第一個(gè)元素
return arr[0];
}
return arr[i];
}
};
int main()
{
safearay A;
cout << "A[2] 的值為 : " << A[2] <<endl;
cout << "A[5] 的值為 : " << A[5]<<endl;
cout << "A[12] 的值為 : " << A[12]<<endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
$ g++ -o test test.cpp
$ ./test
A[2] 的值為 : 2
A[5] 的值為 : 5
A[12] 的值為 : 索引超過(guò)最大值
0
類(lèi)成員訪問(wèn)運(yùn)算符->重載
類(lèi)成員訪問(wèn)運(yùn)算符( -> )可以被重載,但它較為麻煩。它被定義用于為一個(gè)類(lèi)賦予"指針"行為。運(yùn)算符 -> 必須是一個(gè)成員函數(shù)。如果使用了 -> 運(yùn)算符,返回類(lèi)型必須是指針或者是類(lèi)的對(duì)象。
運(yùn)算符 -> 通常與指針引用運(yùn)算符 * 結(jié)合使用,用于實(shí)現(xiàn)"智能指針"的功能。這些指針是行為與正常指針相似的對(duì)象,唯一不同的是,當(dāng)您通過(guò)指針訪問(wèn)對(duì)象時(shí),它們會(huì)執(zhí)行其他的任務(wù)。比如,當(dāng)指針?shù)N毀時(shí),或者當(dāng)指針指向另一個(gè)對(duì)象時(shí),會(huì)自動(dòng)刪除對(duì)象。
間接引用運(yùn)算符 -> 可被定義為一個(gè)一元后綴運(yùn)算符。也就是說(shuō),給出一個(gè)類(lèi):
class Ptr{
//...
X * operator->();
};
類(lèi) Ptr 的對(duì)象可用于訪問(wèn)類(lèi) X 的成員,使用方式與指針的用法十分相似。例如:
void f(Ptr p )
{
p->m = 10 ; // (p.operator->())->m = 10
}
語(yǔ)句 p->m 被解釋為 (p.operator->())->m。同樣地,下面的實(shí)例演示了如何重載類(lèi)成員訪問(wèn)運(yùn)算符 ->。
#include <iostream>
#include <vector>
using namespace std;
// 假設(shè)一個(gè)實(shí)際的類(lèi)
class Obj {
static int i, j;
public:
void f() const { cout << i++ << endl; }
void g() const { cout << j++ << endl; }
};
// 靜態(tài)成員定義
int Obj::i = 10;
int Obj::j = 12;
// 為上面的類(lèi)實(shí)現(xiàn)一個(gè)容器
class ObjContainer {
vector<Obj*> a;
public:
void add(Obj* obj)
{
a.push_back(obj); // 調(diào)用向量的標(biāo)準(zhǔn)方法
}
friend class SmartPointer;
};
// 實(shí)現(xiàn)智能指針,用于訪問(wèn)類(lèi) Obj 的成員
class SmartPointer {
ObjContainer oc;
int index;
public:
SmartPointer(ObjContainer& objc)
{
oc = objc;
index = 0;
}
// 返回值表示列表結(jié)束
bool operator++() // 前綴版本
{
if(index >= oc.a.size()) return false;
if(oc.a[++index] == 0) return false;
return true;
}
bool operator++(int) // 后綴版本
{
return operator++();
}
// 重載運(yùn)算符 ->
Obj* operator->() const
{
if(!oc.a[index])
{
cout << "Zero value";
return (Obj*)0;
}
return oc.a[index];
}
};
int main() {
const int sz = 10;
Obj o[sz];
ObjContainer oc;
for(int i = 0; i < sz; i++)
{
oc.add(&o[i]);
}
SmartPointer sp(oc); // 創(chuàng)建一個(gè)迭代器
do {
sp->f(); // 智能指針調(diào)用
sp->g();
} while(sp++);
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
10
12
11
13
12
14
13
15
14
16
15
17
16
18
17
19
18
20
19
21
C++多態(tài)
多態(tài)按字面的意思就是多種形態(tài)。當(dāng)類(lèi)之間存在層次結(jié)構(gòu),并且類(lèi)之間是通過(guò)繼承關(guān)聯(lián)時(shí),就會(huì)用到多態(tài)。
C++ 多態(tài)意味著調(diào)用成員函數(shù)時(shí),會(huì)根據(jù)調(diào)用函數(shù)的對(duì)象的類(lèi)型來(lái)執(zhí)行不同的函數(shù)。
下面的實(shí)例中,基類(lèi) Shape 被派生為兩個(gè)類(lèi),如下所示:
#include <iostream>
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
class Rectangle: public Shape{
public:
Rectangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Rectangle class area :" <<endl;
return (width * height);
}
};
class Triangle: public Shape{
public:
Triangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
};
// 程序的主函數(shù)
int main( )
{
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// 存儲(chǔ)矩形的地址
shape = &rec;
// 調(diào)用矩形的求面積函數(shù) area
shape->area();
// 存儲(chǔ)三角形的地址
shape = &tri;
// 調(diào)用三角形的求面積函數(shù) area
shape->area();
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Parent class area
Parent class area
導(dǎo)致錯(cuò)誤輸出的原因是,調(diào)用函數(shù) area() 被編譯器設(shè)置為基類(lèi)中的版本,這就是所謂的靜態(tài)多態(tài),或靜態(tài)鏈接 - 函數(shù)調(diào)用在程序執(zhí)行前就準(zhǔn)備好了。有時(shí)候這也被稱(chēng)為早綁定,因?yàn)?area() 函數(shù)在程序編譯期間就已經(jīng)設(shè)置好了。
但現(xiàn)在,讓我們對(duì)程序稍作修改,在 Shape 類(lèi)中,area() 的聲明前放置關(guān)鍵字 virtual,如下所示:
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
virtual int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};
修改后,當(dāng)編譯和執(zhí)行前面的實(shí)例代碼時(shí),它會(huì)產(chǎn)生以下結(jié)果:
Rectangle class area
Triangle class area
此時(shí),編譯器看的是指針的內(nèi)容,而不是它的類(lèi)型。因此,由于 tri 和 rec 類(lèi)的對(duì)象的地址存儲(chǔ)在 *shape 中,所以會(huì)調(diào)用各自的 area() 函數(shù)。
正如您所看到的,每個(gè)子類(lèi)都有一個(gè)函數(shù) area() 的獨(dú)立實(shí)現(xiàn)。這就是多態(tài)的一般使用方式。有了多態(tài),您可以有多個(gè)不同的類(lèi),都帶有同一個(gè)名稱(chēng)但具有不同實(shí)現(xiàn)的函數(shù),函數(shù)的參數(shù)甚至可以是相同的。
虛函數(shù)
虛函數(shù) 是在基類(lèi)中使用關(guān)鍵字 virtual 聲明的函數(shù)。在派生類(lèi)中重新定義基類(lèi)中定義的虛函數(shù)時(shí),會(huì)告訴編譯器不要靜態(tài)鏈接到該函數(shù)。
我們想要的是在程序中任意點(diǎn)可以根據(jù)所調(diào)用的對(duì)象類(lèi)型來(lái)選擇調(diào)用的函數(shù),這種操作被稱(chēng)為動(dòng)態(tài)鏈接,或后期綁定。
純虛函數(shù)
您可能想要在基類(lèi)中定義虛函數(shù),以便在派生類(lèi)中重新定義該函數(shù)更好地適用于對(duì)象,但是您在基類(lèi)中又不能對(duì)虛函數(shù)給出有意義的實(shí)現(xiàn),這個(gè)時(shí)候就會(huì)用到純虛函數(shù)。
我們可以把基類(lèi)中的虛函數(shù) area() 改寫(xiě)如下:
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
// pure virtual function
virtual int area() = 0;
};
= 0 告訴編譯器,函數(shù)沒(méi)有主體,上面的虛函數(shù)是純虛函數(shù)。
C++數(shù)據(jù)抽象
數(shù)據(jù)抽象是指,只向外界提供關(guān)鍵信息,并隱藏其后臺(tái)的實(shí)現(xiàn)細(xì)節(jié),即只表現(xiàn)必要的信息而不呈現(xiàn)細(xì)節(jié)。
數(shù)據(jù)抽象是一種依賴(lài)于接口和實(shí)現(xiàn)分離的編程(設(shè)計(jì))技術(shù)。
讓我們舉一個(gè)現(xiàn)實(shí)生活中的真實(shí)例子,比如一臺(tái)電視機(jī),您可以打開(kāi)和關(guān)閉、切換頻道、調(diào)整音量、添加外部組件(如喇叭、錄像機(jī)、DVD 播放器),但是您不知道它的內(nèi)部實(shí)現(xiàn)細(xì)節(jié),也就是說(shuō),您并不知道它是如何通過(guò)纜線接收信號(hào),如何轉(zhuǎn)換信號(hào),并最終顯示在屏幕上。
因此,我們可以說(shuō)電視把它的內(nèi)部實(shí)現(xiàn)和外部接口分離開(kāi)了,您無(wú)需知道它的內(nèi)部實(shí)現(xiàn)原理,直接通過(guò)它的外部接口(比如電源按鈕、遙控器、聲量控制器)就可以操控電視。
現(xiàn)在,讓我們言歸正傳,就 C++ 編程而言,C++ 類(lèi)為數(shù)據(jù)抽象提供了可能。它們向外界提供了大量用于操作對(duì)象數(shù)據(jù)的公共方法,也就是說(shuō),外界實(shí)際上并不清楚類(lèi)的內(nèi)部實(shí)現(xiàn)。
例如,您的程序可以調(diào)用 sort() 函數(shù),而不需要知道函數(shù)中排序數(shù)據(jù)所用到的算法。實(shí)際上,函數(shù)排序的底層實(shí)現(xiàn)會(huì)因庫(kù)的版本不同而有所差異,只要接口不變,函數(shù)調(diào)用就可以照常工作。
在 C++ 中,我們使用類(lèi)來(lái)定義我們自己的抽象數(shù)據(jù)類(lèi)型(ADT)。您可以使用類(lèi) ostream 的 cout 對(duì)象來(lái)輸出數(shù)據(jù)到標(biāo)準(zhǔn)輸出,如下所示:
#include <iostream>
using namespace std;
int main( )
{
cout << "Hello C++" <<endl;
return 0;
}
在這里,您不需要理解 cout 是如何在用戶(hù)的屏幕上顯示文本。您只需要知道公共接口即可,cout 的底層實(shí)現(xiàn)可以自由改變。
訪問(wèn)標(biāo)簽強(qiáng)制抽象
在 C++ 中,我們使用訪問(wèn)標(biāo)簽來(lái)定義類(lèi)的抽象接口。一個(gè)類(lèi)可以包含零個(gè)或多個(gè)訪問(wèn)標(biāo)簽:
- 使用公共標(biāo)簽定義的成員都可以訪問(wèn)該程序的所有部分。一個(gè)類(lèi)型的數(shù)據(jù)抽象視圖是由它的公共成員來(lái)定義的。
- 使用私有標(biāo)簽定義的成員無(wú)法訪問(wèn)到使用類(lèi)的代碼。私有部分對(duì)使用類(lèi)型的代碼隱藏了實(shí)現(xiàn)細(xì)節(jié)。
訪問(wèn)標(biāo)簽出現(xiàn)的頻率沒(méi)有限制。每個(gè)訪問(wèn)標(biāo)簽指定了緊隨其后的成員定義的訪問(wèn)級(jí)別。指定的訪問(wèn)級(jí)別會(huì)一直有效,直到遇到下一個(gè)訪問(wèn)標(biāo)簽或者遇到類(lèi)主體的關(guān)閉右括號(hào)為止。
數(shù)據(jù)抽象的好處
數(shù)據(jù)抽象有兩個(gè)重要的優(yōu)勢(shì):
- 類(lèi)的內(nèi)部受到保護(hù),不會(huì)因無(wú)意的用戶(hù)級(jí)錯(cuò)誤導(dǎo)致對(duì)象狀態(tài)受損。
- 類(lèi)實(shí)現(xiàn)可能隨著時(shí)間的推移而發(fā)生變化,以便應(yīng)對(duì)不斷變化的需求,或者應(yīng)對(duì)那些要求不改變用戶(hù)級(jí)代碼的錯(cuò)誤報(bào)告。
如果只在類(lèi)的私有部分定義數(shù)據(jù)成員,編寫(xiě)該類(lèi)的作者就可以隨意更改數(shù)據(jù)。如果實(shí)現(xiàn)發(fā)生改變,則只需要檢查類(lèi)的代碼,看看這個(gè)改變會(huì)導(dǎo)致哪些影響。如果數(shù)據(jù)是公有的,則任何直接訪問(wèn)舊表示形式的數(shù)據(jù)成員的函數(shù)都可能受到影響。
數(shù)據(jù)抽象的實(shí)例
C++ 程序中,任何帶有公有和私有成員的類(lèi)都可以作為數(shù)據(jù)抽象的實(shí)例。請(qǐng)看下面的實(shí)例:
#include <iostream>
using namespace std;
class Adder{
public:
// 構(gòu)造函數(shù)
Adder(int i = 0)
{
total = i;
}
// 對(duì)外的接口
void addNum(int number)
{
total += number;
}
// 對(duì)外的接口
int getTotal()
{
return total;
};
private:
// 對(duì)外隱藏的數(shù)據(jù)
int total;
};
int main( )
{
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() <<endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Total 60
上面的類(lèi)把數(shù)字相加,并返回總和。公有成員 addNum 和 getTotal 是對(duì)外的接口,用戶(hù)需要知道它們以便使用類(lèi)。私有成員 total 是用戶(hù)不需要了解的,但又是類(lèi)能正常工作所必需的。
設(shè)計(jì)策略
抽象把代碼分離為接口和實(shí)現(xiàn)。所以在設(shè)計(jì)組件時(shí),必須保持接口獨(dú)立于實(shí)現(xiàn),這樣,如果改變底層實(shí)現(xiàn),接口也將保持不變。
在這種情況下,不管任何程序使用接口,接口都不會(huì)受到影響,只需要將最新的實(shí)現(xiàn)重新編譯即可。
C++數(shù)據(jù)封裝
所有的 C++ 程序都有以下兩個(gè)基本要素:
- 程序語(yǔ)句(代碼):這是程序中執(zhí)行動(dòng)作的部分,它們被稱(chēng)為函數(shù)。
- 程序數(shù)據(jù):數(shù)據(jù)是程序的信息,會(huì)受到程序函數(shù)的影響。
封裝是面向?qū)ο缶幊讨械陌褦?shù)據(jù)和操作數(shù)據(jù)的函數(shù)綁定在一起的一個(gè)概念,這樣能避免受到外界的干擾和誤用,從而確保了安全。數(shù)據(jù)封裝引申出了另一個(gè)重要的 OOP 概念,即數(shù)據(jù)隱藏。
數(shù)據(jù)封裝是一種把數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)捆綁在一起的機(jī)制,數(shù)據(jù)抽象是一種僅向用戶(hù)暴露接口而把具體的實(shí)現(xiàn)細(xì)節(jié)隱藏起來(lái)的機(jī)制。
C++ 通過(guò)創(chuàng)建類(lèi)來(lái)支持封裝和數(shù)據(jù)隱藏(public、protected、private)。我們已經(jīng)知道,類(lèi)包含私有成員(private)、保護(hù)成員(protected)和公有成員(public)成員。默認(rèn)情況下,在類(lèi)中定義的所有項(xiàng)目都是私有的。例如:
class Box
{
public:
double getVolume(void)
{
return length * breadth * height;
}
private:
double length; // 長(zhǎng)度
double breadth; // 寬度
double height; // 高度
};
變量 length、breadth 和 height 都是私有的(private)。這意味著它們只能被 Box 類(lèi)中的其他成員訪問(wèn),而不能被程序中其他部分訪問(wèn)。這是實(shí)現(xiàn)封裝的一種方式。
為了使類(lèi)中的成員變成公有的(即,程序中的其他部分也能訪問(wèn)),必須在這些成員前使用 public 關(guān)鍵字進(jìn)行聲明。所有定義在 public 標(biāo)識(shí)符后邊的變量或函數(shù)可以被程序中所有其他的函數(shù)訪問(wèn)。
把一個(gè)類(lèi)定義為另一個(gè)類(lèi)的友元類(lèi),會(huì)暴露實(shí)現(xiàn)細(xì)節(jié),從而降低了封裝性。理想的做法是盡可能地對(duì)外隱藏每個(gè)類(lèi)的實(shí)現(xiàn)細(xì)節(jié)。
數(shù)據(jù)封裝的實(shí)例
C++ 程序中,任何帶有公有和私有成員的類(lèi)都可以作為數(shù)據(jù)封裝和數(shù)據(jù)抽象的實(shí)例。請(qǐng)看下面的實(shí)例:
#include <iostream>
using namespace std;
class Adder{
public:
// 構(gòu)造函數(shù)
Adder(int i = 0)
{
total = i;
}
// 對(duì)外的接口
void addNum(int number)
{
total += number;
}
// 對(duì)外的接口
int getTotal()
{
return total;
};
private:
// 對(duì)外隱藏的數(shù)據(jù)
int total;
};
int main( )
{
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() <<endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Total 60
上面的類(lèi)把數(shù)字相加,并返回總和。公有成員 addNum 和 getTotal 是對(duì)外的接口,用戶(hù)需要知道它們以便使用類(lèi)。私有成員 total 是對(duì)外隱藏的,用戶(hù)不需要了解它,但它又是類(lèi)能正常工作所必需的。
設(shè)計(jì)策略
通常情況下,我們都會(huì)設(shè)置類(lèi)成員狀態(tài)為私有(private),除非我們真的需要將其暴露,這樣才能保證良好的封裝性。
這通常應(yīng)用于數(shù)據(jù)成員,但它同樣適用于所有成員,包括虛函數(shù)。
C++接口(抽象類(lèi))
接口描述了類(lèi)的行為和功能,而不需要完成類(lèi)的特定實(shí)現(xiàn)。
C++ 接口是使用抽象類(lèi)來(lái)實(shí)現(xiàn)的,抽象類(lèi)與數(shù)據(jù)抽象互不混淆,數(shù)據(jù)抽象是一個(gè)把實(shí)現(xiàn)細(xì)節(jié)與相關(guān)的數(shù)據(jù)分離開(kāi)的概念。
如果類(lèi)中至少有一個(gè)函數(shù)被聲明為純虛函數(shù),則這個(gè)類(lèi)就是抽象類(lèi)。純虛函數(shù)是通過(guò)在聲明中使用 "= 0" 來(lái)指定的,如下所示:
class Box
{
public:
// 純虛函數(shù)
virtual double getVolume() = 0;
private:
double length; // 長(zhǎng)度
double breadth; // 寬度
double height; // 高度
};
設(shè)計(jì)抽象類(lèi)(通常稱(chēng)為 ABC)的目的,是為了給其他類(lèi)提供一個(gè)可以繼承的適當(dāng)?shù)幕?lèi)。抽象類(lèi)不能被用于實(shí)例化對(duì)象,它只能作為接口使用。如果試圖實(shí)例化一個(gè)抽象類(lèi)的對(duì)象,會(huì)導(dǎo)致編譯錯(cuò)誤。
因此,如果一個(gè) ABC 的子類(lèi)需要被實(shí)例化,則必須實(shí)現(xiàn)每個(gè)虛函數(shù),這也意味著 C++ 支持使用 ABC 聲明接口。如果沒(méi)有在派生類(lèi)中重載純虛函數(shù),就嘗試實(shí)例化該類(lèi)的對(duì)象,會(huì)導(dǎo)致編譯錯(cuò)誤。
可用于實(shí)例化對(duì)象的類(lèi)被稱(chēng)為具體類(lèi)。
抽象類(lèi)的實(shí)例
請(qǐng)看下面的實(shí)例,基類(lèi) Shape 提供了一個(gè)接口 getArea(),在兩個(gè)派生類(lèi) Rectangle 和 Triangle 中分別實(shí)現(xiàn)了 getArea():
#include <iostream>
using namespace std;
// 基類(lèi)
class Shape
{
public:
// 提供接口框架的純虛函數(shù)
virtual int getArea() = 0;
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 派生類(lèi)
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
};
class Triangle: public Shape
{
public:
int getArea()
{
return (width * height)/2;
}
};
int main(void)
{
Rectangle Rect;
Triangle Tri;
Rect.setWidth(5);
Rect.setHeight(7);
// 輸出對(duì)象的面積
cout << "Total Rectangle area: " << Rect.getArea() << endl;
Tri.setWidth(5);
Tri.setHeight(7);
// 輸出對(duì)象的面積
cout << "Total Triangle area: " << Tri.getArea() << endl;
return 0;
}
當(dāng)上面的代碼被編譯和執(zhí)行時(shí),它會(huì)產(chǎn)生下列結(jié)果:
Total Rectangle area: 35
Total Triangle area: 17
從上面的實(shí)例中,我們可以看到一個(gè)抽象類(lèi)是如何定義一個(gè)接口 getArea(),兩個(gè)派生類(lèi)是如何通過(guò)不同的計(jì)算面積的算法來(lái)實(shí)現(xiàn)這個(gè)相同的函數(shù)。
設(shè)計(jì)策略
面向?qū)ο蟮南到y(tǒng)可能會(huì)使用一個(gè)抽象基類(lèi)為所有的外部應(yīng)用程序提供一個(gè)適當(dāng)?shù)摹⑼ㄓ玫?、?biāo)準(zhǔn)化的接口。然后,派生類(lèi)通過(guò)繼承抽象基類(lèi),就把所有類(lèi)似的操作都繼承下來(lái)。
外部應(yīng)用程序提供的功能(即公有函數(shù))在抽象基類(lèi)中是以純虛函數(shù)的形式存在的。這些純虛函數(shù)在相應(yīng)的派生類(lèi)中被實(shí)現(xiàn)。
這個(gè)架構(gòu)也使得新的應(yīng)用程序可以很容易地被添加到系統(tǒng)中,即使是在系統(tǒng)被定義之后依然可以如此。
文章內(nèi)容摘自 菜鳥(niǎo)教程http://www.runoob.com/cplusplus/cpp-tutorial.html