Part 1.梳理思維導圖
一.繼承中的特殊成員函數
1.構造函數
父類的構造函數會被繼承到子類中,在構造的順序中,是先構造父類,再構造子類
#include <iostream>using namespace std;class Father
{
public:string name;
protected:int *age;
private:int weight;
public:Father(){age = nullptr;cout << "Father::無參構造函數" << endl;}Father(string n,int a,int w):name(n),age(new int(a)),weight(w){cout << "Father::有參構造函數" << endl;}void show(){cout << name << " " << *age << " " << weight << endl;}
};class Son:public Father
{
private:string sonname;int *sonage;
public:Son(){sonage = nullptr;cout << "Son::無參構造函數" << endl;}Son(string sn,int sa,string fn,int a,int w):Father(fn,a,w),sonname(sn),sonage(new int(sa)){cout << "Son::有參構造函數" << endl;}void show(){cout << sonname << " " << *sonage << " ";Father::show();}
};int main()
{Son s1("張四",18,"張三",40,100);s1.show();return 0;
}
在創建子類時,會先調用父類的構造函數,再調用子類的構造函數
2.析構函數
父類的析構函數會被繼承到子類中,在析構的順序中,先析構子類,再析構父類
#include <iostream>using namespace std;class Father
{
public:string name;
protected:int *age;
private:int weight;
public:Father(){age = nullptr;cout << "Father::無參構造函數" << endl;}Father(string n,int a,int w):name(n),age(new int(a)),weight(w){cout << "Father::有參構造函數" << endl;}~Father(){delete age;age = nullptr;cout << "Father::析構函數" << endl;}void show(){cout << name << " " << *age << " " << weight << endl;}
};class Son:public Father
{
private:string sonname;int *sonage;
public:Son(){sonage = nullptr;cout << "Son::無參構造函數" << endl;}Son(string sn,int sa,string fn,int a,int w):Father(fn,a,w),sonname(sn),sonage(new int(sa)){cout << "Son::有參構造函數" << endl;}~Son(){delete sonage;sonage = nullptr;cout << "Son::析構函數" << endl;}void show(){cout << sonname << " " << *sonage << " ";Father::show();}
};int main()
{Son s1("張四",18,"張三",40,100);s1.show();return 0;
}
在函數結束時,釋放子類,會先調用子類的析構函數,再調用父類的析構函數
3.拷貝構造函數
父類的拷貝構造函數函數會被繼承到子類中,如果有深拷貝的問題,子類父類完成各自的拷貝工作
#include <iostream>using namespace std;class Father
{
public:string name;
protected:int *age;
private:int weight;
public:Father(){age = nullptr;cout << "Father::無參構造函數" << endl;}Father(string n,int a,int w):name(n),age(new int(a)),weight(w){cout << "Father::有參構造函數" << endl;}~Father(){delete age;age = nullptr;cout << "Father::析構函數" << endl;}Father(const Father &other):name(other.name),age(new int(*(other.age))),weight(other.weight){cout << "Father::拷貝構造函數" << endl;}void show(){cout << name << " " << *age << " " << weight << endl;}
};class Son:public Father
{
private:string sonname;int *sonage;
public:Son(){sonage = nullptr;cout << "Son::無參構造函數" << endl;}Son(string sn,int sa,string fn,int a,int w):Father(fn,a,w),sonname(sn),sonage(new int(sa)){cout << "Son::有參構造函數" << endl;}~Son(){delete sonage;sonage = nullptr;cout << "Son::析構函數" << endl;}Son(const Son &other):Father(other),sonname(other.sonname),sonage(new int(*(other.sonage))){cout << "Son::拷貝構造函數" << endl;}void show(){cout << sonname << " " << *sonage << " ";Father::show();}
};int main()
{Son s1("張四",18,"張三",40,100);s1.show();Son s2 = s1;s2.show();return 0;
}
在子類對象就行拷貝構造時,會調用子類的拷貝構造函數拷貝子類的內容,并調用父類的拷貝構造函數拷貝父類內容,最后構造另一個子類對象
4.拷貝賦值函數
父類的拷貝賦值函數函數會被繼承到子類中,如果有深拷貝的問題,子類父類完成各自的拷貝工作
#include <iostream>using namespace std;class Father
{
public:string name;
protected:int *age;
private:int weight;
public:Father(){age = nullptr;cout << "Father::無參構造函數" << endl;}Father(string n,int a,int w):name(n),age(new int(a)),weight(w){cout << "Father::有參構造函數" << endl;}Father &operator=(const Father &other){if(this != &other){name = other.name;age = new int(*(other.age));weight = other.weight;}cout << "Father::拷貝賦值函數" << endl;return *this;}void show(){cout << name << " " << *age << " " << weight << endl;}
};class Son:public Father
{
private:string sonname;int *sonage;
public:Son(){sonage = nullptr;cout << "Son::無參構造函數" << endl;}Son(string sn,int sa,string fn,int a,int w):Father(fn,a,w),sonname(sn),sonage(new int(sa)){cout << "Son::有參構造函數" << endl;}Son &operator=(const Son &other){if(this != &other){sonname = other.sonname;sonage = new int(*(other.sonage));Father::operator = (other);}cout << "Son::拷貝賦值函數" << endl;return *this;}void show(){cout << sonname << " " << *sonage << " ";Father::show();}
};int main()
{Son s1("張四",18,"張三",40,100);s1.show();Son s3;s3 = s1;s3.show();return 0;
}
二.多繼承
1.概念
一個子類繼承了多個父類
2.格式
class 類名: 繼承方式1 類名1,繼承方式2 類名2,...,繼承方式n 類名n
{子類的拓展;
};
3.例子
#include <iostream>using namespace std;class Sofa
{
private:string sit;
public:Sofa(){cout << "Sofa::無參構造函數" << endl;}Sofa(string sit):sit(sit){cout << "Sofa::有參構造函數" << endl;}~Sofa(){cout << "Sofa::析構函數" << endl;}void show(){Jiaju::show();cout << sit << endl;}
};class Bed
{
private:string sleep;
public:Bed(){cout << "Bed::無參構造函數" << endl;}Bed(string sleep):sleep(sleep){cout << "Bed::有參構造函數" << endl;}~Bed(){cout << "Bed::析構函數" << endl;}void show(){Jiaju::show();cout << sleep << endl;}
};class Bedsofa:public Sofa,public Bed
{
private:string color;
public:Bedsofa(){cout << "Bedsofa::無參構造函數" << endl;}Bedsofa(string sit,string sleep,string color,):Sofa(sit),Bed(sleep),color(color){cout << "Bedsofa::有參構造函數" << endl;}~Bedsofa(){cout << "Bedsofa::析構函數" << endl;}void show(){Bed::show();Sofa::show();cout << color << endl;}
};int main()
{Bedsofa b1("能坐","能躺","白色",100);b1.show();return 0;
}
4.小結
多繼承可以使一個子類擁有多個父類的數據成員與成員函數,由于是多個父類,所以再構造子類對象時,會先構建父類;
在子類的構造函數中,注意調用父類的順序,該順序就是在子類頭部聲明調用父類的順序
三.菱形繼承
1.概念
一個公共基類生出多個中間子類,這些中間子類生出一個匯聚子類,這就是菱形繼承
(一個父類生出多個子類,這些子類合出一個孫類)
2.例子
#include <iostream>using namespace std;
class Jiaju
{
private:int weight;
public:Jiaju(){cout << "Jiaju::無參構造函數" << endl;}Jiaju(int weight):weight(weight){cout << "Jiaju::有參構造函數" << endl;}~Jiaju(){cout << "Jiaju::析構函數" << endl;}void show(){cout << weight << endl;}
};class Sofa:public Jiaju
{
private:string sit;
public:Sofa(){cout << "Sofa::無參構造函數" << endl;}Sofa(string sit,int weight):Jiaju(weight),sit(sit){cout << "Sofa::有參構造函數" << endl;}~Sofa(){cout << "Sofa::析構函數" << endl;}void show(){Jiaju::show();cout << sit << endl;}
};class Bed:public Jiaju
{
private:string sleep;
public:Bed(){cout << "Bed::無參構造函數" << endl;}Bed(string sleep,int weight):Jiaju(weight),sleep(sleep){cout << "Bed::有參構造函數" << endl;}~Bed(){cout << "Bed::析構函數" << endl;}void show(){Jiaju::show();cout << sleep << endl;}
};class Bedsofa:public Sofa,public Bed
{
private:string color;
public:Bedsofa(){cout << "Bedsofa::無參構造函數" << endl;}Bedsofa(string sit,string sleep,string color,int weight):Sofa(sit,weight),Bed(sleep,weight),color(color){cout << "Bedsofa::有參構造函數" << endl;}~Bedsofa(){cout << "Bedsofa::析構函數" << endl;}void show(){Bed::show();Sofa::show();cout << color << endl;}
};int main()
{Bedsofa b1("能坐","能躺","白色",100);b1.show();return 0;
}
在該函數中,公共基類(jiaju)生出兩個中間子類(bed和sofa),最后倆中間子類生出一個匯聚子類(bedsofa),匯聚子類用到了多繼承,繼承兩個父類及兩個中間子類,倆中間子類的共同父類及公共基類
3.菱形繼承的弊端及分析
????????A.弊端
1.匯聚子類會得到多分由中間子類繼承下來的公共基類,這樣會浪費空間
2.匯聚子類對象創建時,會多次調用公共基類的構造函數和析構函數
? ? ? ? B.原因
1.因為子類創建時都會調用父類,在匯聚子類創建時,會調用自己的父類及中間子類,bedsofa創建類對象按順序會調用sofa和bed的構造函數,但是sofa和bed會先調用自己父類及公共基類的構造函數,按調用順序是:jiaju - sofa - jiaju - bed - bedsofa,所以在構造和析構時,jiaju的構造函數和析構函數都會被多次調用。如果函數中有指針類型成員,在析構時會多次delete空間,會出現錯誤
2.按上述所講,子類會繼承父類所有數據成員和成員函數,有兩個中間子類,匯聚子類就會繼承到兩次公共基類
四.虛繼承
1.作用
解決菱形繼承的錯誤,讓多個中間子類只繼承一次公共基類
2.格式
在中間子類繼承前面加上virtual
class 類名:virtual 繼承方式 類名 // 中間子類
{中間子類分拓展;
};
3.注意事項
1.由于中間子類中的公共基類太多,編譯器不清楚應該保存公共基類哪些成員函數,所以會直接保存公共基類的無參構造函數,所以如果需要公共基類的有參構造函數,需要在匯集子類里手動調用
2.所有虛繼承的中間子類會返回一個公共基類,如果有中間子類沒有虛繼承,那個中間子類會返回公共基類,所以需要把所有中間子類都虛繼承
4.示例
#include <iostream>using namespace std;
class Jiaju
{
private:int weight;
public:Jiaju(){cout << "Jiaju::無參構造函數" << endl;}Jiaju(int weight):weight(weight){cout << "Jiaju::有參構造函數" << endl;}~Jiaju(){cout << "Jiaju::析構函數" << endl;}void show(){cout << weight << endl;}
};class Sofa:virtual public Jiaju//虛繼承
{
private:string sit;
public:Sofa(){cout << "Sofa::無參構造函數" << endl;}Sofa(string sit,int weight):Jiaju(weight),sit(sit){cout << "Sofa::有參構造函數" << endl;}~Sofa(){cout << "Sofa::析構函數" << endl;}void show(){Jiaju::show();cout << sit << endl;}
};class Bed:virtual public Jiaju//虛繼承
{
private:string sleep;
public:Bed(){cout << "Bed::無參構造函數" << endl;}Bed(string sleep,int weight):Jiaju(weight),sleep(sleep){cout << "Bed::有參構造函數" << endl;}~Bed(){cout << "Bed::析構函數" << endl;}void show(){Jiaju::show();cout << sleep << endl;}
};class Bedsofa:public Sofa,public Bed
{
private:string color;
public:Bedsofa(){cout << "Bedsofa::無參構造函數" << endl;}Bedsofa(string sit,string sleep,string color,int weight):Jiaju(weight),Sofa(sit,weight),Bed(sleep,weight),color(color)//匯聚子類手動調用公共基類{cout << "Bedsofa::有參構造函數" << endl;}~Bedsofa(){cout << "Bedsofa::析構函數" << endl;}void show(){Bed::show();Sofa::show();cout << color << endl;}
};int main()
{Bedsofa b1("能坐","能躺","白色",100);b1.show();return 0;
}