1、類的組合:一個類內嵌其他類的對象作爲成員的情況,他們之間的關係是一種包含與被包含的關係。當創建一個類的對象時,如果這個類具有內嵌對象成員,那麼這個內嵌對象將首先被自動創建,因此在創建對象時既要對本類的基本類型數據成員進行初始化,又要對內嵌對象成員進行初始化。
組合類構造函數定義的一般形式爲:類名::類名(形參表):內嵌對象1(形參表),內嵌對象2(形參表),......
2、創建一個組合類的對象時,不僅他自身的構造函數將被調用,而且還將調用其內嵌對象的構造函數,此時構造函數調用順序是:
a、調用內嵌對象的構造函數,調用順序按照內嵌對象在組合類的定義中出現的次序。
b、執行本類構造函數的函數體
若聲明組合類的對象時沒有指定對象的初始值,則默認形式(無形參)的構造函數被調用,這是內嵌對象的默認形式構造函數也被調用。析構函數的調用執行順序與構造函數剛好相反。
3、對一個類,若沒有編寫拷貝構造函數,編譯系統會在必要時自動生成一個默認的拷貝構造函數,若建立組合類的對象時調用默認拷貝構造函數,則編譯器將自動調用內嵌的拷貝構造函數。如果要爲組合類編寫拷貝構造函數,則需要爲內嵌成員對象的拷貝構造函數傳遞參數,例如C類中包含B類對象b作爲成員,C類的拷貝構造函數形式如下:
C::C(C &c1):b(c1.b){.......}接下來讓我們看一個實例:
class Point
{
public :
Point(int xx = 0, int yy = 0){X = xx; Y = yy;}
Point(Point &p);
int GetX(){return X;}
int GetY(){return Y;}
private:
int X,Y;
};
Point ::Point(Point &p)
{
X = p.X;
Y = p.Y;
cout<<"Point 拷貝構造函數被調用"<<endl;
}
class Line
{
public:
Line (Point xp1, Point xp2);
Line (Line &);
double GetLen(){return len;}
private:
Point p1,p2;
double len;
};
Line::Line(Point xp1,Point xp2):p1(xp1),p2(xp2)
{
cout<<"Line 構造函數被調用"<<endl;
double x = double(p1.GetX() - p2.GetX());
double y = double(p1.GetY() - p2.GetY());
len = sqrt(x * x + y * y );
}
Line::Line(Line &L):p1(L.p1) ,p2(L.p2)
{
cout<<"Line 拷貝構造函數被調用"<<endl;
len = L.len;
}
int main()
{
Point myq1(1,1),myq2(4,5);
Line line(myq1,myq2);
Line line2(line);
return 0;
}
其結果輸出爲:
Point 拷貝構造函數被調用
Point 拷貝構造函數被調用
Point 拷貝構造函數被調用
Point 拷貝構造函數被調用
Line 構造函數被調用
Point 拷貝構造函數被調用
Point 拷貝構造函數被調用
Line 構造函數被調用
4、前向引用聲明
對於兩個類相互引用的情況,也稱爲循環依賴,需要前向引用,如:
class A class B
{ {
public: public:
void f(B b); void g(A a);
} }
那麼在類A中的成員函數f中的形式參數是類B的對象,此時編譯會引起錯誤,可在class A前加上類B的前向引用:class B;
但是在提供一個完整類定義之前,不能定義該類的對象,也不能在內聯成員函數中使用該類的對象。即當使用前向引用聲明時,只能使用被聲明的符號,而不能使用涉及到類的任何細節。
5、類模板與函數模版
對於功能相同但是成員的類型不同的類
a、使用template<class T>前向聲明,之後在類定義中,欲採用通用數據類型的數據成員、成員函數的參數或返回值,前面需加上T
b、在類定義外定義成員函數時,若此成員函數中有模版參數存在,則需要在函數體外進行模版聲明,並在函數名前的類名後綴上加上“<T>”,如
template<class T>
T 類名<T>::function(){return number;}
c、類模板不代表一個具體的、實際的類,而代表一類類。實際上,類模板的使用就是將類模板實例化成一個具體的類,格式爲:
類名<實際的類型>對象名;
對於函數模版也類似,即採用template<typename T>,之後直接用 T function (T x,T y)
6、UML類圖
UML規定數據成員表示的語法爲:【訪問控制屬性】名稱【重數】【:類型】【=默認值】【|約束條件|】
至少必須指定數據成員的名稱,其他的都是可選的,其中:
訪問控制屬性:分爲public 、private 、protected 三種,分別對應於UML中的“+”、“-”、“#”
UML規定函數成員表示爲:【訪問控制屬性】名稱【(參數表)】【:返回類型】【約束特性】
7、類的靜態數據成員
a、靜態成員解決了同一個類的不同對象之間數據的和函數共享問題,面向對象方法中還有“類屬性”的概念,如果某個屬性爲整個類所共有,不屬於任何一個具體對象,則採用static關鍵字來聲明爲靜態成員,靜態成員在每一個類只有一份拷貝,由該類所有對象共同維護和使用,從而實現同一類不同對象之間的數據共享。
b、靜態成員具有靜態生存期,只能通過類名對他進行訪問,即類名::標識符,在類的定義中僅僅對靜態數據成員進行聲明,必須在文件作用域的某個地方使用類名限定對靜態數據成員進行定義性說明(此時系統分配空間),這時也可以進行初始化。在UML語音中靜態數據成員通過在數據成員下方添加下劃線表示。
c、靜態數據成員通常應該通過非內聯函數來訪問,因爲編譯器確保在調用任何一個非內聯函數之前初始化靜態數據成員。但當從另一個編譯單元通過內聯函數調用靜態成員時,靜態成員有可能尚未被初始化,這樣的調用是不安全的。
d、公有靜態成員可以在類外通過類名進行訪問。
e、如果要通過非靜態函數來訪問靜態數據成員,應該使用非內聯函數,而且訪問靜態數據成員的函數,其函數體的定義應該與靜態成員的初始化寫在同一個源程序文件中。
8、類的靜態函數成員
a、對普通函數成員的調用必須通過對象名,但是可以對於靜態函數成員可通過類名來訪問,對於公有的靜態成員函數,可以通過類名或者對象名來調用。
b、靜態成員函數可以直接訪問該類的靜態數據和靜態函數成員,而訪問非靜態數據成員必須通過參數傳遞方式得到對象名,然後通過對象名來訪問。
c、在UML中靜態函數成員是通過在函數成員前添加《static》構造來表徵。
9、友元
a、友元是c++提供的一種破壞數據結構封裝和數據隱藏的機制
b、通過將一個模塊聲明爲另一個模塊的友元,一個模塊能夠引用到另一個模塊中本事被隱藏的信息(如private、protected成員),但訪問對象的成員必須通過對象名
c、如果友元是一般成員或類的成員函數,稱爲友元函數,若友元的是一個類,則稱友元類
d、一個類的成員函數可以是另一個類的友元,即一個類的成員函數可以說明爲另一個類的友元函數,以便通過該成員函數訪問到另一個類的成員
e、若一個類爲另一個類的友元,則此類的所有成員都能訪問對方的私有成員。將友元類名在另一個類中使用friend修飾說明。
g、友元沒有傳遞性,且是單向的。
10、常類型
常類型的對象必須進行初始化,而且不能被更新。
a、常引用:被引用的對象不能被更新:const 類型說明符 &引用名
b、常對象:必須進行初始化,不能被更新:類名 const 對象名
c、常數組:數組元素不能被更新:類型說明符 const 數組名[大小]...
d:常指針:指向常量的指針
11、常成員函數與常數據成員
a、使用const關鍵字說明的函數,常成員函數不更新對象的數據成員,常成員函數說明格式:類型說明符 函數名 (參數表)const;
const關鍵字可以被用於參與對重載函數的區分,通過常對象只能調用它的常成員函數,其他成員函數不能調用。
b、常使用const說明的數據成員,對於常數據成員的初始化只能用類似A::A(int i):a(i),r(i)的方法進行初始化,而不能直接賦值。