文章目錄
- 繼承簡介
- 定義
- 訪問限定符和繼承方式
- ?基類派生類賦值轉換
- 繼承的作用域
- 派生類的默認成員函數
- 繼承與友元
- 繼承與靜態成員
- ?復雜的菱形繼承
- 虛擬繼承
- 組合
繼承簡介
繼承是面向對象程序設計代碼復用的重要手段,使得程序員可以在保持原類的基礎上擴展,新擴展的類叫派生類,體現類的層級結構,之前的重載是函數復用,繼承是類的復用。
定義
#include <iostream>
#include <string>
using namespace std;
class person
{
public:void print(){cout << _name << " " << _sex << " " << _age << endl;;}
protected:string _name ="Jack";string _sex ="man";int _age=18;
};
class student : public person
{
protected:int _number;
};
int main()
{person p1;student s1;p1.print();s1.print();return 0;
}
可以看到,student是派生類,public是繼承方式,person是基類
派生類擁有基類的所有成員
訪問限定符和繼承方式
總結:
1,基類的private成員,無論什么繼承,派生類都不可見,但是成員還是繼承到了派生類中,但是語法限制了派生類訪問
2,基類的private在派生類不可見,要想突破限制,就用protected,在類外不可訪問,但在派生類可訪問,訪問限制符protected是為繼承準備的
3,觀察上面表格,可以發現兩個不同限定符,取范圍更小的,如protected和private 取private
4,class類默認繼承方式private,struct類默認public,一般都寫出繼承方式的
5,一般都是public繼承,幾乎很少private/protected,
?基類派生類賦值轉換
🚩 派生類對象可以賦值基類對象/基類引用/基類指針,這個過程叫切片,意為著把派生類那塊基類所有的切給基類,
🚩基類對象不能賦值給派生類
如圖,派生可以賦值給基類,只不過 _number消失了
int main()student s2;person p2 = s2;person* pp2 = &s2;person& ppp2 = s2;student* ss2 = (student*)pp2;//這種強轉可以pp2 = &p2;//student* ss2 = (student*)pp2;//這種會越界訪問return 0;
繼承的作用域
class person
{
public:void print(){cout << "基類" << endl;}
};
class student : public person
{
public:void print(){cout << "派生類" << endl;}
};
int main()
{student s3;s3.print();s3.student::print();s3.person::print();return 0;
}
1,基類和派生類都有獨立的作用域
🚩2,基類與派生類成員重名時,子類調用會直接跳過父類成員,這叫隱藏,也叫重定義,或者可用 類名::成員 訪問
🚩3,在一個作用域的重名叫重載,兩個作用域重名叫隱藏
4,繼承體系最好不要定義重名成員
派生類的默認成員函數
1,派生類對象初始化先調用基類構造函數,若基類無默認構造函數,則要在初始化列表顯式調用,再調用自己的構造函數
2,構造,賦值,拷貝構造都是先調基類再調子類
3,析構先調子類再基類
#include <iostream>
#include <string>
using namespace std;
class person
{
public:person(string name ="jack"):_name(name){cout << "person()" << endl;}person(const person& p1):_name(p1._name){cout << "person(& p1)" << endl;}person& operator=(const person& p1){cout << "operator(const person&p1)" << endl;if(this!=&p1){_name = p1._name;}return *this;}~person(){ }
protected:string _name;
};
class student : public person
{
public:student(const string name="peter", int number = 110):person(name),_number(number){cout << "student()" << endl;}student(const student& s):person(s),_number(s._number){cout << "student(student& s)" << endl;}student& operator=(const student& s){if (this != &s){person::operator= (s);_number = s._number;}cout << "operator=(const student &s)" << endl;return *this;}~student(){ }
protected:int _number;
};
int main()
{person p1("peter");student s1("jack", 11);student s2;s2 = s1;return 0;
}
繼承與友元
友元不能繼承,父類的朋友不是我的朋友
所以父類友元不能訪問子類成員,除非子類也聲明友元
#include <iostream>
#include <string>
using namespace std;
class student;//聲明
class person {
public:friend void Display(const person& p, const student& s);
protected:string _name = "person";
};class student : public person {
public:friend void Display(const person& p, const student& s); // 子類也聲明為友元student(int num = 0) : _number(num) {}
protected:int _number;
};void Display(const person& p, const student& s) {cout << p._name << endl;cout << s._number << endl; // 現在可以訪問(student 的友元)
}int main() {person p;student s(12345);Display(p, s);return 0;
}
繼承與靜態成員
🚩基類定義static靜態成員,則整個繼承體系只有一個這樣的成員,無論多少派生多少子類,只有這一個靜態成員
#include <iostream>
#include <string>
using namespace std;
class person
{
public:person() {++_count;}string _name;static int _count;
};
int person ::_count = 0;
class student:public person
{
public:student():person(){++_count;}
};
int main()
{student s;cout << person::_count<<endl;return 0;
}
2
?復雜的菱形繼承
單繼承:一個子類只有一個父類
多繼承:一個子類有兩個及以上的父類
菱形繼承:多繼承的一種
下圖,可看出菱形繼承數據冗余和二義性問題,有兩份person值
#include <iostream>
#include <string>
using namespace std;
class person
{
public:string _name;
};
class student :public person
{
public:string _num;
};
class teacher :public person
{
public:string _id;
};
class assistant :public student, public teacher
{
public:string _majorcourse;
};
int main()
{assistant a;//a._name = "peter";//?不明確a.student::_name = "peter";//可以指明訪問,a.teacher::_name = "jack";return 0;
}
指明訪問解決了二義性,但數據冗余還是沒解決,
虛擬繼承
虛擬繼承解決了二義性和數據冗余問題,
class person
{
public:string _name;
};
class student :virtual public person
{
public:string _num;
};
class teacher :virtual public person
{
public:string _id;
};
class assistant :virtual public student, virtual public teacher
{
public:string _majorcourse;
};
原理:
只存一個person,student和teacher中的person 存到assistant的地址偏移量,用時再跳過去找
注意:
菱形虛擬繼承底層復雜,一般不設計多繼承,更不能設計菱形繼承
組合
繼承是 is-a關系
組合是 has-a關系
- 繼承復用 基類對子類公布了成員細節,俗稱白箱復用,一定程度上破壞了封裝,且基類 改動對子類影響較大,子類和基類關聯性強,耦合度高
- 組合復用 被組合對象要求有良好接口,基類成員細節不可見,俗稱黑箱復用,不破壞封裝,關聯性低,耦合度低
實戰中能用組合就用組合,耦合度低,代碼便于維護,不過有些情況就適合繼承,多態也需要繼承實現,可以用繼承和組合就用組合
class car
{
protected:string _colour;string _num;
};class Benz :public car
{
public:void Drive(){cout << "奢華" << endl;}
};
繼承關系
class tire
{
public:string _brand;int _size;
};
class car {string _colour;string _num;//車牌tire _t;//輪胎
};
組合關系