1. 程式人生 > >C++學習筆記7_多態

C++學習筆記7_多態

替換 空間 更多 區別 類構造 重要 protect func 標記

1. 類與類之間的關系
class A
{
public:
int a;
void funcA()
{}
}
包含:
class B
{
public:
void funcB(){}
A a;
}
//如果類B有類A的成員變量,那麽B has A,類B依賴於類A
使用:
class C
{
public:
void funC(A *a)
{
}
}
//C的成員方法需要A的形參,C use A

*B與A的耦合度比C要高。
//
繼承:
class D:public A
{
void funcD()
{

}
D(int a):A(a) //與C#不同的地方,C#是D(int a):base(a),可能是C++有多重繼承的原因
{
//..
}
}
*D si A,耦合度最高。
//為什麽要寫public 呢?應該是確定能否通過D來訪問A的成員???

has A,use A, is A;

2.
A->B->C
A->B1->C1
B->D
B1->D
D是多繼承

3.類的繼承方式

這是和C#不同的地方,C#沒有繼承方式,繼承就是繼承了,就算有,C#都是public繼承, A中的成員的“外部和繼承可訪問性”,繼承後還是一樣。

C++通過繼承方式,限制了繼承後,改變了原來A“對外和繼承的可訪問性”。

更多是private和protect的繼承方式,對原來A中public的影響最大

*一般用public就行了,其他繼承方式不建議使用。

4. 類的兼容原則
class A;
class Aplus:public A;
int main()
{
Aplus ap;
A a=ap;
}
//跟C#類似,能裏氏替換,c#子類引用變量能賦值給父類引用變量;
//C++因為子類有父類的所有東西,所以能給父類的所有字段初始化;
//父類指針可以指向子類對象
//父類引用可以引用子類對象
//左父=右子(右子賦值(=號操作符)給左父,右子初始化左父(構造函數))
//子類能夠滿足父類指針、引用、變量的全部需求。反之不行。

5. 子類構造析構
構造函數,跟C#一樣,會先調用父類的構造函數

6. 繼承中,子類的成員和父類成員有重名:
子類內部
void func()
{
this->a=1;
A::a=1;//註意,這裏要和使用靜態的成員區分。
}
//一般都避免這樣
//C++的繼承其實是很粗暴的,內存的開辟直接堆上去,不管重不重名,有就堆上去。
//C#中,子類的字段不能和父類同名,屬性也是,如果非要同名,要加public new int Age{ get;set;}

7.
class A
{
public:
static int a;//靜態變量在靜態區,繼承方式是影響不了其訪問性
}
int A::a=100;//靜態變量初始化
//
int main()
{
A a;
cout<<a.a<<endl;
}

8. 多繼承
//
class Furniture
{
public : int price;
}
class Sofa:public Furniture;
class Bed:public Furniture;
class SofaBed:public Bed,public Sofa;
//
那SofaBed就有兩個price空間的大小了(c++粗暴的堆上去),那只能
int main()
{
SofaBed sb;
sb.Bed::price=100;
sb.Sofa::price=200;
}
//這是和C#不同的地方,C#沒有多繼承

9. 虛繼承 (一般很少這樣做)
**要不想這樣子,給兩個price賦值,那麽只能:
class Sofa:vitual public Furniture;//加virtual
class Bed:vitual public Furniture;//加virtual
class SofaBed:public Bed,public Sofa;//不變
####出現棱形繼承#####
**父類繼承爺爺類的時候加virtual
**virtual繼承,只是為了開辟“爺類”的字段一次
#區別開C#中的virtual方法,C#中virtual只是用來標記方法,能被子類重新,在以父類或者子類引用的方式調用時,均會執行子類的重寫的方法。

10. C++中的虛方法(重要)
class A {
public:
function()
{
//...
}
};
class Aplus:public A
{
public:
function()
{
//...
}
}
//
int main()
{
A *a=new Aplus();//左父右子
a->Function();//這裏是調用了A的fuction,不寫成虛方法,就執行指針類型的funtion;
}
(這種又叫重定義,調用以A的指針調用function,那麽執行A的function;以Aplus的指針調用function,那麽執行Aplus的function。)
//這裏又和C#不同了,同名方法沒有標記為virtual都可以繼承。
//C++是父類型指針變量,指向子類對象,能調用子類方法。
***如果想象C#一樣,用父類變量引用子類對象,依然可以執行子類的方法。
解決辦法:
class A {
public:
vitual function()//虛方法
{
//...
}
};
//這裏又和C#不同了,子類不需要在方法前寫override
****************但是,習慣上也在子類的方法前加virtual,做一個標識作用,就當做C#中的override****************
//在.cpp文件中,實現虛方法,就不需要加virtual了。

11. 虛析構函數(重要)
防止在使用“左父-右子”的父指針 delete father,如果不標記析構函數為virtual(原因見下),只能析構父類的空間。子類寫成virtual析構,能兩個析構都調用,不用自己再在子析構中,顯式調用父析構。
***特別是用工廠方法new對象時,較為要緊*********
*在正常析構子類對象時,父類對象能自動析構

***********繼承也要註意父析構函數標記virtual**********

13. vtpr指針(重要)。
vtpr指針,是指向虛函數表的指針。
例如:A中有virtual fun1(); Aplus重寫;
fun1會放到A的虛函數表;fun1會放到Aplus的虛函數表;

A a=new Aplus();
a->fun1();
編譯器發現fun1()是虛的,應該會額外標記(重要步驟)。在運行的時候,vtpr指針會去虛函數表找fun1的函數,因為a實質上是Aplus,所以實質上是去找Aplus的虛函數表。
//所以,如果A的fun1沒標記為virtual,那麽編譯自然不會額外標記了,在運行時就只按照A類查找普通的函數表;
//小知識,同樣的類,有函數加了virtual,那麽sizeof(A)會莫名增大,因為多了vtpr指針的大小。
//不要在父類構造函數中調用virtual函數,否則子類vtpr指針還沒初始化好,調的依然是父類的函數,vtpr指針是隨著各個父類的構造函數,不斷變換指向,直到最後的子類的。

14. 指針步長
A array[] = {Aplus(0),{Aplus(1),{Aplus(2)};//相當於C#A[] array = {new Aplus(0),..};
可以使用array代表是指向第一個元素的地址;
array++可以指向第二個元素;但是,要註意的是,array++是按A的內存大小,往下跳的,如果Aplus所占用的大小大於A,那麽array++會跳到一個錯誤的位置。

15. 純虛函數,抽象類(C++沒有C#中的接口,但是有純虛函數)
class shape{
public:
double height;
double weight;
virtual int func()=0;//=0只是一個語法,標記為純虛函數。並不是返回值
shape(){}
virtual ~shape(){}//抽象類最好提供一下虛析構
}
//如果一個類有純虛函數,那麽就是抽象類,不管有沒有普通函數。
//跟C#一樣,抽象類不能實例化。
//跟C#一樣,抽象類能繼承抽象類。
/ /跟C#不一樣,C#的抽象類必須寫成abstract,而且可以寫virtual函數,virtual函數能有實現,反而抽象函數,要標記開abstract

class A:public Interface;

C++學習筆記7_多態