派生類繼承構造函數
如果基類含多個構造函數,多數情況下,派生類一般會集成所有構造函數,以下幾種情況比較特殊
- 1、如果你在派生類中定義的構造函數與基類構造函數有
相同的參數列表
,那麼從基類中繼承的會被覆蓋掉
也就是只繼承了及部分構造函數
- 2、拷貝,移動構造函數不會被繼承
- 3、using classA::classA,這不屬於自己定義構造函數,編譯器還是會合成一個默認的無參構造函數,但是這樣聲明瞭之後,父類必須有無參構造函數
自己管自己的父類
一個類只繼承直接基類的(父類)的構造函數
。默認,拷貝,移動構造函數不能被集成
class classA
{
public:
//classA(int i, int j, int k){}
classA(int i, int j, int k = 0){}
classA() {}
};
class classB:public classA
{
public:
//classB(int i, int j, int k) :classA(i, j, k){}
using classA::classA;
};
這樣看來,using就是讓某個函數名字可見
遇到這條代碼是,編譯器會把基類中每個構造函數
,都生成一個與之對應的構造函數
但是到底長什麼樣呢?
classB(參數列表):classA(照抄父類參數列表){}
,比如這個
classB(int i,int j,int k):classA(i,j,k){}
如果有默認參數的話
,那麼編譯器遇到using classA::classA的時候
就會幫我們在派生類中構造出多個構造函數出來
- 1、帶所有參數的構造函數
- 2、其餘分別少一個默認參數
例如classA(int i, int j, int k = 0)
如果使用using classA::classA,那麼就會有如下的默認構造函數生成
1、classB(int i , int j , int k):A(i,j,k){}
2、classB(int i , int j):A(i,j){}
多重集成
class grandFather
{
public:
int m_gf;
public:
//這裏參數列表賦值
grandFather(int i) :m_gf(i)
{
cout << "grandFather構造函數" << endl;
}
void myInfo()
{
cout << m_gf << endl;
}
virtual ~grandFather()
{
cout << "grandFather析構函數" << endl;
}
};
class father :public grandFather
{
public:
int m_f;
public:
//每個子類的構造函數,負責解決自己父類的初始化問題
//每個類都要解決直接父類的初始化問題
//不然只會調用默認的無參構造函數
//想一想,java的雙親委託機制是不是就是這樣實現的?
father(int i) :m_f(i), grandFather(m_f)
{
cout << "father構造函數" << endl;
}
virtual ~father()
{
cout << "father析構函數" << endl;
}
void myInfo()
{
cout << m_f << endl;
}
};
class others
{
public:
int m_o;
public:
//每個子類的構造函數,負責解決自己父類的初始化問題
others(int i) :m_o(i)
{
cout << "others構造函數" << endl;
}
virtual ~others()
{
cout << "others析構函數" << endl;
}
void myInfo()
{
cout << m_o << endl;
}
};
//派生列表
class classSon :public father, public others
{
public:
int m_s;
public:
//有兩個直接父類,都要把他們初始化
classSon(int i, int j, int k) :father(i), others(j),m_s(k)
{
cout << "classSon構造函數" << endl;
}
virtual ~classSon()
{
cout << "classSon析構函數" << endl;
}
//因爲父類函數衝突了,子類重寫覆蓋
void sonInfo()
{
cout << m_s << endl;
}
//遇到衝突的問題,可以直接在子類中,進行
//父類::函數/參數
void sonCallParentInfo()
{
father::myInfo();
others::myInfo();
sonInfo();
}
};
- 再多重繼承中,派生類會包含每個基類的子對象
int main()
{
classSon class1(1, 2, 3);
class1.sonInfo();
}
執行結果:
- father覆蓋了grandFather中的myInfo();如果子類繼承的多個類中都有myInfo這個函數,就要顯示聲明調用的是哪個函數
classSon class1(1, 2, 3);
class1.father::myInfo();
class1.others::myInfo();
靜態成員變量
屬於類,不屬於對象
但是對象也是可以調用的
在grandFather中加上
class grandFather
{
public:
static int gf_SI;
}
//爲了能夠使用,要定義這個靜態類
//沒有用到就不用定義,使用必須定義,光聲明是不行的
int grandFather::gf_SI = 666;
int main()
{
cout << grandFather::gf_SI << endl;
cout << father::gf_SI << endl;
cout << classSon::gf_SI << endl;
//對象也是可以的
cout << class1.gf_SI << endl;
}
派生類構造函數與析構函數
- 1、構造一個派生類對象,將同時構造初始化所有基類子對象
- 2、派生類的初始化列表只初始化它的直接基類,這樣所有的都能初始化
- 3、派生類構造函數吃石化列表將實參分別傳遞給每個直接基類,基類的構造順序由派生列表決定,派生列表:
class classSon :public father, public others
,跟初始化列表(classSon(int i, int j, int k) :father(i), others(j),m_s(k)
)沒有關係
顯式的初始化與隱士的初始化(使用默認構造函數,不帶參數)
//新建了一個類others2,只有無參構造方法
class others2
{
public:
//是默認的無參構造方法
others2()
{
cout << "others2構造函數" << endl;
}
virtual ~others2()
{
cout << "others2析構函數" << endl;
}
};
class classSon :public father, public others,public others2
{
public:
int m_s;
public:
//雖然繼承了others2類,但是由於只有無參構造函數,不顯式初始化也是可以的
classSon(int i, int j, int k) :father(i), others(j),m_s(k)
{}
}
繼承的兩個構造函數參數重複,會導致程序錯誤,必須定義自己的版本
class A
{
public:
A(int val) {};
};
class B
{
public:
B(int val) {};
};
class C :public A, public B
{
public:
//這樣繼承了A,B的構造函數,但是一摸一樣(只要參數列表一樣就是一摸一樣)
using A::A;
using B::B;
//必須定義自己的參數列表一樣的去覆蓋
C(int val) :A(val), B(val) {}
};
解釋爲什麼基類指針可以指向子類對象
基類指針了一指向派生類對象,編譯器會幫助我們進行轉換,因爲每個派生類對象
都包含
一個基類對象
區域,所以基類的指針和引用可以綁定到基類上
grandFather* g = new classSon(1, 2, 3);
虛基類,虛繼承
派生列表中同一類只能出現一次,但是以下情況特別
- 1、通過兩個直接基類,繼承了兩次間接基類
- 2、直接繼承該基類,通過直接繼承另一個類,又間接繼承了該類
初始化多次,名字衝突,不能直接調用那個類的方法,因爲不知道用哪一個的
如下:
我們重寫others類,讓它也和father一樣繼承grandfather類
class others:public grandFather
{
public:
int m_o;
public:
//初始化grandFather
others(int i) :m_o(i) ,grandFather(i)
{
cout << "others構造函數" << endl;
}
virtual ~others()
{
cout << "others析構函數" << endl;
}
void myInfo()
{
cout << m_o << endl;
}
};
int main()
{
classSon class1(1,2,3);
}
然後classSon類實際上通過father,others類繼承了兩次grandfather類,看一看初始化classSon類的效果
真的構造了兩次,不行不行
解決方法:虛繼承
首先,虛基類:無論這個類在繼承體系中出現多少次,派生類中都只會包含唯一一個虛基類子內容
這種虛繼承不對直接子類有意義,只對直接子類派生出來的類纔有意義
最後所有虛基類的子類都要虛繼承,才能保證孫類是虛繼承
格式:class classA:virtual father,並且要用孫類的構造函數進行初始化
class classSon : public father, public others
{
public:
//如果父類是虛繼承,那麼初始化虛基類的任務就要交給最小輩兒的類來
classSon(int i, int j, int k) :grandFather(k),father(i), others(j),m_s(k)
{cout << "classSon構造函數" << endl;}
}
// 聲明虛繼承
class others:virtual public grandFather
{
public:
// 這裏初始化也沒什麼用了
others(int i) :m_o(i) ,grandFather(i)
}
// 聲明虛繼承
class father :virtual public grandFather
{
public:
// 這裏初始化也沒什麼用了
father(int i) :m_f(i), grandFather(m_f)
}
int main()
{
classSon class1(1,2,3);
}
執行效果:
說明兩點:
- 1、現在是孫子類構造函數初始化,如果以後它再有子類,那麼也要進行加上換句話說,就是最底層的進行初始化虛基類
- 2、虛基類不管派生列表的順序,它就是首先進行初始化
- 3、多個虛基類,從派生列表的繼承順序,往回追溯,先找到那個虛基類,就先初始化哪個
總結:
- 小心虛繼承,不太提倡使用
- 簡單,不易出現二義性,實在必要才用。