中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

做網(wǎng)站如何選擇數(shù)據(jù)源永久免費(fèi)進(jìn)銷存管理軟件手機(jī)版

做網(wǎng)站如何選擇數(shù)據(jù)源,永久免費(fèi)進(jìn)銷存管理軟件手機(jī)版,給小學(xué)生做家教的網(wǎng)站,網(wǎng)站設(shè)計(jì)網(wǎng)站瀏覽目錄 一、前言 二、什么是繼承 ? 💢繼承的概念💢 💢繼承的定義💢 🥝定義格式 🍇繼承權(quán)限 三、基類與派生類對象的賦值轉(zhuǎn)換 四、繼承的作用域 五、派生類中的默認(rèn)成員函數(shù) &#x1f4a2…

目錄

一、前言

二、什么是繼承 ??

💢繼承的概念💢?

💢繼承的定義💢??

🥝定義格式?

🍇繼承權(quán)限?

三、基類與派生類對象的賦值轉(zhuǎn)換?

四、繼承的作用域?

五、派生類中的默認(rèn)成員函數(shù)?

💢默認(rèn)成員函數(shù)的調(diào)用?💢

🔥構(gòu)造函數(shù)與析構(gòu)函數(shù)🔥

🔥拷貝構(gòu)造🔥?

🔥賦值運(yùn)算符重載?🔥

💢顯示成員函數(shù)的調(diào)用?💢?

🔥構(gòu)造函數(shù)?🔥

🔥拷貝構(gòu)造?🔥

🔥賦值運(yùn)算符重載?🔥

🔥析構(gòu)函數(shù)🔥?

六、繼承與友元?

七、繼承與靜態(tài)成員

八、菱形繼承?

💧 單繼承💧

💧 多繼承💧

💧 菱形繼承💧?

🍍概念?

🍉現(xiàn)象?

九、繼承和組合?

十、繼承的總結(jié)和反思?

十一、共勉?


一、前言

? ?繼承?是 面向?qū)ο笕筇匦灾?/span>(封裝、繼承、多態(tài)),所有的面向?qū)ο?#xff08;OO)語言都具備這三個(gè)基本特征,封裝相關(guān)概念已經(jīng)在《類和對象》系列中介紹過了,今天主要學(xué)習(xí)的是?繼承即如何在父類的基礎(chǔ)之上,構(gòu)建出各種功能更加豐富的子


二、什么是繼承 ??

什么是繼承?是繼承 -- 遺產(chǎn) 還是繼承 -- 花唄?答案都不是,先來看看官方解釋:

繼承(inheritance)機(jī)制是 ----面向?qū)ο蟪绦蛟O(shè)計(jì)使代碼可以復(fù)用的重要的手段,它允許程序員在保持原有基類(父類)特性的基礎(chǔ)上進(jìn)行擴(kuò)展,增加功能,這樣產(chǎn)生新的類,稱為派生類(子類)?


💢繼承的概念💢?

繼承相關(guān)概念:?

  • 被繼承對象:父類 / 基類 (base
  • 繼承方:子類 / 派生類 (derived

繼承的本質(zhì)?就是 ------------?復(fù)用代碼?


舉個(gè)例子 : 假設(shè)我現(xiàn)在要設(shè)計(jì)一個(gè)校園管理系統(tǒng),那么肯定會設(shè)計(jì)很多角色類,比如學(xué)生老師、保安、保潔等等之類的。?

設(shè)計(jì)好以后,我們發(fā)現(xiàn),有些數(shù)據(jù)和方法是每個(gè)角色都有的,而有些則是每個(gè)角色獨(dú)有的。?

為了復(fù)用代碼、提高開發(fā)效率,可以從各種角色中選出共同點(diǎn),組成 基類,比如每個(gè) 人 都有姓名、年齡、聯(lián)系方式等基本信息,而 教職工學(xué)生 的區(qū)別就在于 管理與被管理,因此可以在 基類 的基礎(chǔ)上加一些特殊信息如教職工號 表示 教職工,加上 學(xué)號 表示學(xué)生,其他細(xì)分角色設(shè)計(jì)也是如此

這樣就可以通過?繼承?的方式,復(fù)用 基類 的代碼,劃分出各種 子類?

?像上面共同擁有的數(shù)據(jù)和方法我們可以重新設(shè)計(jì)一個(gè)類Person ,然后讓 StudentTeacher 去繼承它,如下:

// 大眾類  --- 基礎(chǔ)屬性
class Person
{
public:Person(string name = string(), string tell = string(), int age = int()):_name(name),_tell(tell),_age(age){}void Print(){cout << "我的名字是 :" << _name << endl;cout << "我的電話是 :" << _tell << endl;cout << "我的年齡是 :" << _age << endl;}
protected:string _name; // 姓名string _tell; // 電話int _age; // 年齡
};// 學(xué)生類  --- 派生/子屬性
class Student : public Person
{
public:Student(int stuId = 1578):Person("XAS","123456789",26),_stuId(stuId){cout << "我是一個(gè)學(xué)生" << endl;cout << "以下是我的個(gè)人信息 :" << endl;cout << endl;cout << "我的學(xué)號為 :" << _stuId << endl;}
protected:int _stuId; // 學(xué)號
};// 老師類  --- 派生/子屬性
class Teacher : public Person
{
public:Teacher(int workId = 2024916):Person("xas","987654321",26),_workId(workId){cout << "我是一個(gè)老師" << endl;cout << "以下是我的個(gè)人信息 :" << endl;cout << endl;cout << "我的工號為 :" << _workId << endl;}
protected:int _workId; // 工號
};int main()
{Student s;s.Print();cout << "---------------------" << endl;cout << "---------------------" << endl;Teacher t;t.Print();return 0;
}

繼承后父類的 Person 成員(成員函數(shù)+成員變量)都會變成子類的一部分。這里體現(xiàn)出了Student Teacher 復(fù)用了 Person?


💢繼承的定義💢??

了解完 繼承相關(guān)概念 后,就可以開始學(xué)習(xí) 使用繼承??

🥝定義格式?

格式如下: Person 是?父類,也稱作基類; Student子類,也稱作派生類。?

格式為 :子類 : 繼承方式 父類,比如?class a : public b?就表示?a?繼承了?b,并且還是?公有繼承?

注:Java?中的繼承符號為?extern,而?C++?中為?:?


🍇繼承權(quán)限?

繼承有權(quán)限的概念,分別為:公有繼承(public)、保護(hù)繼承(protected)、私有繼承(private?

沒錯(cuò),與??中的訪問?限定修飾符?一樣,不過這些符號在這里表示?繼承權(quán)限?

簡單回顧下各種限定符的用途

  • 公有 public:公開的,任何人都可以訪問
  • 保護(hù) protected:保護(hù)的,只有當(dāng)前類和子類可以訪問
  • 私有 private:私有的,只允許當(dāng)前類進(jìn)行訪問

權(quán)限大小:公有 > 保護(hù) > 私有
保護(hù) protected 比較特殊,只有在 繼承 中才能體現(xiàn)它的價(jià)值,否則與 私有 作用一樣

此時(shí)我們發(fā)現(xiàn)? ---- 訪問權(quán)限三種? ? ?繼承權(quán)限三種

根據(jù)排列組合,可以列出以下多種搭配方案?

父類成員 / 繼承權(quán)限publicprotectedprivate
父類的?public?成員外部可見,子類中可見外部不可見,子類中可見外部不可見,子類中可見
父類的?protected?成員外部不可見,子類中可見外部不可見,子類中可見外部不可見,子類中可見
父類的?private?成員都不可見都不可見都不可見

注:所謂的--外部--其實(shí)就是 子類對象

總結(jié):

  • 無論是哪種繼承方式,父類中的?private?成員始終不可被 [子類 / 外部] 訪問;當(dāng)外部試圖訪問父類成員時(shí),依據(jù)?min(父類成員權(quán)限, 子類繼承權(quán)限),只有最終權(quán)限為?public?時(shí),外部才能訪問
  • 在實(shí)際運(yùn)用中一般使用都是 public 繼承,幾乎很少使用 protetced/private 繼承,也不提倡使用?protetced/private繼承,因?yàn)?protetced/private 繼承下來的成員都只能在派生類的類里面使用,實(shí)際中擴(kuò)展維護(hù)性不強(qiáng)

如何證明呢??通過一下的代碼,我們來驗(yàn)證上面的結(jié)論!!

// 父類
class A
{
public:int _a;
protected:int _b;
private:int _c;
};// 子類
class B : public A
{
public:B(){cout << _a << endl;cout << _b << endl;cout << _c << endl;}
};int main()
{// 外部(子類對象)B b;      b._a;
}
  • public 繼承?

  • ?protected?繼承?

  • ??private?繼承?

之所以說?C++?的繼承機(jī)制設(shè)計(jì)復(fù)雜了,是因?yàn)?protected?和?private?繼承時(shí)的效果一樣

其實(shí)?C++?中搞這么多種情況(9種)完全沒必要,實(shí)際使用中,最常見到的組合為?public : public?和?protected : public?


如何優(yōu)雅的使用好? ---- 繼承權(quán)限?

對于只想自己類中查看的成員,設(shè)為?private,對于想共享給子類使用的成員,設(shè)為?protected其他成員都可以設(shè)為?public

比如在張三家中,張三家的房子面積允許公開,家庭存款只限家庭成員共享,而個(gè)人隱私數(shù)據(jù)則可以設(shè)為私有

class Home
{
public:int area = 500;	//500 平米的大房子
};class Father : public Home
{
protected:int money = 50000;	//存款五萬
private:int privateMoney = 100;	//私房錢,怎能公開?
};class Zhangsan : public Father
{
public:Zhangsan(){cout << "我是張三" << endl;cout << "我知道我家房子有 " << area << " 平方米" << endl;cout << "我也知道我家存款有 " << money << endl;cout << "但我不知道我爸爸的私房錢有多少" << endl;}
};class Xiaoming
{
public:Xiaoming(){cout << "我是小明" << endl;cout << "我只知道張三家房子有 " << Home().area << " 平方米" << endl;cout << "其他情況我一概不知" << endl;}
};int main()
{Zhangsan z;cout << "================" << endl;Xiaoming x;return 0;
}


三、基類與派生類對象的賦值轉(zhuǎn)換?

在繼承中,允許將 子類 對象直接賦值給 父類,但不允許 父類 對象賦值給 子類?

  • 這其實(shí)很好理解,兒子以后可以當(dāng)父親,父親還可以當(dāng)兒子嗎?

并且這種?賦值?是非常自然的,編譯器直接處理,不需要調(diào)用?賦值重載?等函數(shù)?


派生類對象? ?可以賦值給? ? ? 基類的對象 / 基類的指針 / 基類的引用。?

(1) 子類對象可以賦值給父類對象?

//基類
class Person
{
public:string _name; // 姓名string _sex; // 性別int _age; // 年齡
};//派生類
class Student : public Person
{
public:int _id;
};int main()
{Person p;Student s;s._name = "張三";s._sex = "男";s._age = 20;s._id = 8888;p = s; // 子類對象賦值給父類對象return 0;
}

?通過調(diào)式可以看到,為什么沒有把 id 賦值過去呢?

這里有個(gè)形象的說法叫切片或者切割,相當(dāng)于把派生類中父類那部分切來賦值過去,如圖所示:?


(2) 子類對象可以賦值給父類指針??

//基類
class Person
{
public:string _name; // 姓名string _sex; // 性別int _age; // 年齡
};//派生類
class Student : public Person
{
public:int _id;
};int main()
{Student s;s._name = "張三";s._sex = "男";s._age = 20;s._id = 8888;Person* p = &s;return 0;
}

可以看到,當(dāng)父類對象是一個(gè)指針的時(shí)候,照樣可以賦值過去:

子類對象賦值給父類指針切片圖:?


(3) 子類對象可以賦值給父類引用?

//基類
class Person
{
public:string _name; // 姓名string _sex; // 性別int _age; // 年齡
};//派生類
class Student : public Person
{
public:int _id;
};int main()
{Student s;s._name = "張三";s._sex = "男";s._age = 20;s._id = 8888;Person& rp = s;return 0;
}

?可以看到,當(dāng)父類對象是一個(gè)引用的時(shí)候,也可以賦值過去:

?子類對象賦值給父類引用切換圖片:


(4) 父類對象不能賦值給子類對象

//基類
class Person
{
public:string _name; // 姓名string _sex; // 性別int _age; // 年齡
};//派生類
class Student : public Person
{
public:int _id;
};int main()
{Student s;Person p;s = p;return 0;
}

編譯會報(bào)錯(cuò):?


四、繼承的作用域?

在繼承體系中 基類派生類 都有獨(dú)立的作用域,如果子類和父類中有同名成員子類成員將屏蔽父類對同名成員的直接訪問,這種情況叫?隱藏,也叫重定義。

代碼示例: Student 的 _num Person的 _num 構(gòu)成隱藏關(guān)系,可以看出這樣代碼雖然能跑,但是非常容易混淆。

// 基類
class Person
{
protected:string _name = "Edison"; // 姓名int _num = 555; // 身份證號
};// 派生類
class Student : public Person
{
public:void Print(){cout << "姓名:" << _name << endl;cout << "學(xué)號:" << _num << endl;}
protected:int _num = 888; // 學(xué)號
};int main()
{Student s1;s1.Print();return 0;
}

?運(yùn)行可以看到,訪問的是子類中的_num?(類似于局部優(yōu)先的原則)

那么如果我想訪問父類中的_num 呢 ? 可以使用基類 :: 基類成員顯示的去訪問 :?

// 基類
class Person
{
protected:string _name = "Edison"; // 姓名int _num = 555; // 身份證號
};// 派生類
class Student : public Person
{
public:void Print(){cout << "姓名:" << _name << endl;cout << " 身份證號:" << Person::_num << endl;}
protected:int _num = 888; // 學(xué)號
};int main()
{Student s1;s1.Print();return 0;
}

?可以看到,此時(shí)就是訪問的父類中的 _num?

還有一點(diǎn)需要注意的是 : 如果是成員函數(shù)的隱藏,只需要函數(shù)名相同就構(gòu)成隱藏。?

// 基類
class A
{
public:void fun(){cout << "A::func()" << endl;}
};// 派生類
class B : public A
{
public:void fun(int i){cout << "B::func()" << endl;cout << "func(int i)->" << i << endl;}
};int main()
{B b;b.fun(10);return 0;
}

?可以看到,默認(rèn)是去調(diào)用子類的 fun() 函數(shù),因?yàn)槌蓡T函數(shù)滿足函數(shù)名相同就構(gòu)成隱藏。

如果想調(diào)用父類的 fun() 還是需要指定作用域?

// 基類
class A
{
public:void fun(){cout << "A::func()" << endl;}
};// 派生類
class B : public A
{
public:void fun(int i){cout << "B::func()" << endl;cout << "func(int i)->" << i << endl;}
};int main()
{B b;b.A::fun();return 0;
}

?運(yùn)行可以看到,此時(shí)就是調(diào)用父類中的 fun()

注意 :B 中的 fun 和 A 中的 fun 不是構(gòu)成函數(shù)重載,而是隱藏 !函數(shù)重載的要求是在同一作用域里面!!

另外,在實(shí)際中在繼承體系里面最好不要定義同名的成員。


五、派生類中的默認(rèn)成員函數(shù)?

派生類(子類)也是?,同樣會生成?六個(gè)默認(rèn)成員函數(shù)(用戶未定義的情況下)

不同于單一的?子類?是在?父類?的基礎(chǔ)之上創(chuàng)建的,因此它在進(jìn)行相關(guān)操作時(shí),需要為?父類?進(jìn)行考慮

這里我們只以下面的兩個(gè)類為基礎(chǔ),討論四類默認(rèn)成員函數(shù):構(gòu)造函數(shù)、拷貝構(gòu)造、賦值運(yùn)算符重載、析構(gòu)函數(shù)?

class Person
{
public:Person(const std::string name = std::string(), const int age = 18, const int sex = 1): _name(name), _age(age), _sex(sex){std::cout << "Person()" << std::endl;}Person(const Person& p): _name(p._name), _age(p._age), _sex(p._sex){std::cout << "Person(const Person& p)" << std::endl;}Person& operator= (const Person& p){std::cout << "operator= (const Person& p)" << std::endl;if (this != &p){_name = p._name;}return *this;}~Person(){std::cout << "~Person()" << std::endl;}
protected:std::string _name;int _age = 18;int _sex = 1;
};class Student : public Person
{
public:
protected:long long _st_id;
};

💢默認(rèn)成員函數(shù)的調(diào)用?💢

🔥構(gòu)造函數(shù)與析構(gòu)函數(shù)🔥

int main()
{Student st;return 0;
}

output:?

Person()     // s1 的構(gòu)造
~Person()    // s1 的析構(gòu)

說明了,派生類的默認(rèn)構(gòu)造函數(shù)和默認(rèn)析構(gòu)函數(shù)都會 -----自動(dòng)調(diào)用基類的構(gòu)造和析構(gòu)?


🔥拷貝構(gòu)造🔥?

int main()
{Student st1;Student st2(st1);return 0;
}

?output:

Person()                 // s1 的構(gòu)造函數(shù)
Person(const Person& p)  // s2 拷貝構(gòu)造
~Person()                // s2 的析構(gòu)函數(shù)
~Person()                // s1 的析構(gòu)函數(shù)

說明了,派生類的默認(rèn)拷貝構(gòu)造會自動(dòng)調(diào)用基類的拷貝構(gòu)造?


🔥賦值運(yùn)算符重載?🔥

int main()
{Student st1, st2;st1 = st2;return 0;
}

output:

Person()    // s1 的拷貝構(gòu)造
Person()    // s2 的拷貝構(gòu)造
operator= (const Person& p)    // 賦值重載
~Person()   // s2 的析構(gòu)函數(shù)
~Person()   // s1 的析構(gòu)函數(shù)

說明了,派生類的默認(rèn)賦值運(yùn)算符重載會自動(dòng)調(diào)用基類的賦值運(yùn)算符重載?


實(shí)際上,我們可以將派生類的成員分成三部分:基類成員、內(nèi)置類型成員、自定義類型成員?

  • 繼承相較于我們以前學(xué)的類和對象,可以說就是多了基類那一部分

  • 當(dāng)調(diào)用派生類的默認(rèn)成員函數(shù)時(shí),對于基類成員都會調(diào)用對應(yīng)基類的默認(rèn)成員函數(shù)來處理


💢顯示成員函數(shù)的調(diào)用?💢?

🔥構(gòu)造函數(shù)?🔥

當(dāng)實(shí)現(xiàn)派生類的構(gòu)造函數(shù)時(shí),就算不顯示調(diào)用基類的構(gòu)造,系統(tǒng)也會自動(dòng)調(diào)用基類的構(gòu)造?

class Student : public Person
{
public:Student(long long st_id = 111): _st_id(st_id){std::cout << "Student()" << std::endl;}
protected:long long _st_id;
};
int main()
{Student st;return 0;
}

?output:

Person()
Student()
~Person()

如果需要顯示的調(diào)用基類的構(gòu)造函數(shù),應(yīng)該這樣寫:?

Student(long long st_id = 111): _st_id(st_id), Person("xas", 18)
{std::cout << "Student()" << std::endl;
}

?特別注意:

Person("xas", 18)如果放在初始化列表,那就使顯式調(diào)用基類的構(gòu)造函數(shù);如果放在函數(shù)體內(nèi),那就使創(chuàng)建一個(gè)基類的匿名對象?


🔥拷貝構(gòu)造?🔥

?當(dāng)實(shí)現(xiàn)派生類的拷貝構(gòu)造時(shí),如果沒有顯式調(diào)用基類的拷貝構(gòu)造,那么系統(tǒng)就會自動(dòng)調(diào)用基類的構(gòu)造

class Student : public Person
{
public:Student(long long st_id = 111): _st_id(st_id), Person("xas", 18){std::cout << "Student()" << std::endl;}Student(const Student& s): _st_id(s._st_id){std::cout << "Student(const Student& s)" << std::endl;}
protected:long long _st_id;
};
int main()
{Student st1;Student st2(st1);return 0;
}

output:?

Person()
Student()
Person()	//系統(tǒng)自動(dòng)調(diào)用了基類的構(gòu)造
Student(const Student& s)
~Person()
~Person()

?也可以像顯示調(diào)用構(gòu)造函數(shù)一樣顯式調(diào)用基類的拷貝構(gòu)造:

Student(const Student& s): Person(s), _st_id(s._st_id)
{std::cout << "Student(const Student& s)" << std::endl;
}

🔥賦值運(yùn)算符重載?🔥

?在實(shí)現(xiàn)派生類的賦值運(yùn)算符重載時(shí),如果沒有顯式調(diào)用基類的賦值運(yùn)算符重載,系統(tǒng)也不會自動(dòng)調(diào)用基類的賦值運(yùn)算符重載

class Student : public Person
{
public:Student(long long st_id = 111): _st_id(st_id), Person("xas", 18){std::cout << "Student()" << std::endl;}Student& operator= (const Student& s){std::cout << "operator= (const Student& s)" << std::endl;if (this != &s){_st_id = s._st_id;}return *this;}
protected:long long _st_id;
};
int main()
{Student st1, st2;st1 = st2;return 0;
}

output:?

Person()
Student()
Person()
Student()
operator= (const Student& s)
~Person()
~Person()

因此,在實(shí)現(xiàn)派生類的賦值運(yùn)算符重載時(shí),必須顯示調(diào)用基類的賦值運(yùn)算符重載:?

Student& operator= (const Student& s)
{std::cout << "operator= (const Student& s)" << std::endl;if (this != &s){Person::operator=(s);	//由于基類和派生類的賦值運(yùn)算符重載構(gòu)成隱藏,因此要用 :: 指定類域_st_id = s._st_id;}return *this;
}

🔥析構(gòu)函數(shù)🔥?

?在實(shí)現(xiàn)派生類的析構(gòu)函數(shù)時(shí),不要顯式調(diào)用基類的析構(gòu)函數(shù),系統(tǒng)會在派生類的析構(gòu)完成后自動(dòng)調(diào)用基類的析構(gòu)

class Student : public Person
{
public:Student(long long st_id = 111): _st_id(st_id), Person("xas", 18){std::cout << "Student()" << std::endl;}~Student(){std::cout << "~Student()" << std::endl;}
protected:long long _st_id;
};int main()
{Student st1;return 0;
}

output:?

Person()
Student()
~Student()
~Person()
  • 和前面的默認(rèn)成員函數(shù)不同,在實(shí)現(xiàn)派生類的析構(gòu)時(shí),基類的析構(gòu)不能顯式調(diào)用
  • 這是因?yàn)?#xff0c;如果顯示調(diào)用了基類的析構(gòu),就會導(dǎo)致基類成員的資源先被清理,如果此時(shí)派生類成員還訪問了基類成員指向的資源就,就會導(dǎo)致野指針問題
  • 因此,必須保證析構(gòu)順序?yàn)?strong>先子后父,保證數(shù)據(jù)訪問的安全

六、繼承與友元?

友元關(guān)系不能繼承,也就是說 基類友元不能訪問子類私有和保護(hù)成員,只能訪問自己的私有和保護(hù)成員。?

下面代碼中,Display 函數(shù)是基類 Person 的友元,但是 Display 函數(shù)不是派生類 Student 的友元,也就是說 Display 函數(shù)無法訪

class Student;class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string _name; // 姓名
};class Student : public Person
{
protected:int _stuNum; // 學(xué)號
};void Display(const Person& p, const Student& s)
{cout << p._name << endl; // 可以訪問cout << s._stuNum << endl; // 無法訪問
}int main()
{Person p;Student s;Display(p, s);return 0;
}

可以看到運(yùn)行會報(bào)錯(cuò):

如果想讓 Display 函數(shù)也能夠訪問派生類Student 的私有和保護(hù)成員,只需要在派生類Student 當(dāng)中進(jìn)行友元聲明。?

class Student;class Person
{
public:friend void Display(const Person& p, const Student& s); // 聲明Display是Person的友元
protected:string _name; // 姓名
};class Student : public Person
{
public:friend void Display(const Person& p, const Student& s); // 聲明Display是Student的友元
protected:int _stuNum; // 學(xué)號
};void Display(const Person& p, const Student& s)
{cout << p._name << endl; // 可以訪問cout << s._stuNum << endl; // 可以訪問
}int main()
{Person p;Student s;Display(p, s);return 0;
}

七、繼承與靜態(tài)成員

?如果基類中定義了static 靜態(tài)成員,則整個(gè)繼承體系里面只有一個(gè)這樣的成員。 無論派生出多少個(gè)子類,都只有一個(gè) static 成員實(shí)例。

下面代碼中,在基類 Person 當(dāng)中定義了靜態(tài)成員變量 _ count, 派生類 StudentGraduate 繼承了Person, 但是,在整個(gè)繼承體?

// 基類
class Person
{
public:Person() { ++_count; }
protected:string _name; // 姓名
public:static int _count; // 統(tǒng)計(jì)人的個(gè)數(shù)。
};// 靜態(tài)成員在類外面定義
int Person::_count = 0; // 派生類
class Student : public Person
{
protected:int _stuNum; // 學(xué)號
};// 派生類
class Graduate : public Student
{
protected:string _seminarCourse; // 研究科目
};int main()
{Student s1;Student s2;Student s3;Graduate s4;Person s;cout << " 人數(shù) :" << Person::_count << endl;cout << " 人數(shù) :" << Student::_count << endl;cout << " 人數(shù) :" << s4._count << endl;return 0;
}

?我們定義了5個(gè)對象,那么每定義一個(gè)對象都會去調(diào)用一次++ _count ,打印以后可以看到,這幾個(gè)對象里面的 _count 都是一樣的:

同時(shí),我們還可以打印一下地址,可以看到也是同-個(gè):?

?總結(jié):?關(guān)于父類中的靜態(tài)成員,子類繼續(xù)下來以后都是同一個(gè),類似于“傳家寶"。


八、菱形繼承?

💧 單繼承💧

一個(gè)子類只有一個(gè)直接父類時(shí)稱這個(gè)繼承關(guān)系為單繼承???

💧 多繼承💧

?一個(gè)子類有兩個(gè)或以上直接父類時(shí)稱這個(gè)繼承關(guān)系為多繼承


💧 菱形繼承💧?

?C++?支持多繼承,即支持一個(gè)子類繼承多個(gè)父類,使其基礎(chǔ)信息更為豐富,但凡事都有雙面性多繼承 在帶來巨大便捷性的同時(shí),也帶來了個(gè)巨大的坑:菱形繼承問題

🍍概念?

首先?C++?允許出現(xiàn)多繼承的情況,如下圖所示?

這樣看很正常是吧,但如果出現(xiàn)以下這種?重復(fù)繼承?的情況,就比較麻煩了?

此時(shí)?普通人X?會糾結(jié)于使用哪一個(gè)?不用吃飯?的屬性!這對于編譯器來說,是一件無法處理的事?

🍉現(xiàn)象?

將上述概念轉(zhuǎn)化為代碼,觀察實(shí)際現(xiàn)象

注:多繼承時(shí),只需要在 父類 之后,添加?,?號,繼續(xù)增加想要繼承的父類

class Person
{
public:string _name;	//姓名
};//本科生
class Undergraduate : public Person
{};//研究生
class Postgraduate : public Person
{};//畢業(yè)生
class Graduate : public Undergraduate, public Postgraduate
{};int main()
{Graduate g1;g1._name = "zhangsan";return 0;
}

無法編譯!?


原因分析:?

?Undergraduate?中繼承了?Person?的?_namePostgraduate?也繼承了?Person?的?_name

Graduate?多繼承?Undergraduate?、Postgraduate?后,同時(shí)擁有了兩個(gè)?_name,使用時(shí),無法區(qū)分!

通過監(jiān)視窗口查看信息:?


解決方法:?

想要解決二義性很簡單,通過?::?限制訪問域即可?

Graduate g1;
g1.Undergraduate::_name = "zhangsan";
cout << g1.Undergraduate::_name << endl;

但這沒有從本質(zhì)上解決問題!而且還沒有解決數(shù)據(jù)冗余問題?

?真正的解決方法:虛繼承

?注:虛繼承是專門用來解決 菱形繼承 問題的,與多態(tài)中的虛函數(shù)沒有直接關(guān)系


虛繼承:在菱形繼承的腰部繼承父類時(shí),加上?virtual?關(guān)鍵字修飾被繼承的父類?

class Person
{
public:string _name;	//姓名
};//本科生
class Undergraduate : virtual public Person
{};//研究生
class Postgraduate : virtual public Person
{};//畢業(yè)生
class Graduate : public Undergraduate, public Postgraduate
{};int main()
{Graduate g1;g1._name = "zhangsan";cout << g1._name << endl;return 0;
}

此時(shí)可以解決?菱形繼承?的?數(shù)據(jù)冗余?和?二義性?問題?

?虛繼承是如何解決菱形繼承問題的?

  • 利用?虛基表?將冗余的數(shù)據(jù)存儲起來,此時(shí)冗余的數(shù)據(jù)合并為一份
  • 原來存儲 冗余數(shù)據(jù) 的位置,現(xiàn)在用來存儲?虛基表指針

此時(shí)無論這個(gè) 冗余 的數(shù)據(jù)存儲在何處,都能通過 基地址 + 偏移量 的方式進(jìn)行訪問?


九、繼承和組合?

public 繼承是一種 is-a 的關(guān)系。也就是說每個(gè)派生類對象都是一個(gè)基類對象。?
組合 是一種 has-a 的關(guān)系。假設(shè) B 組合了 A,每個(gè) B對象中都有一個(gè) A對象。

舉個(gè)例子: 轎車和奔馳就構(gòu)成 is-a 的關(guān)系,所以可以使用繼承。?

// 車類
class Car
{
protected:string _colour = "黑色"; // 顏色string _num = "川A66688"; // 車牌號
};// 奔馳
class Benz : public Car
{
public:void Drive(){cout << "好開-操控" << endl;}
};

再舉個(gè)例子:汽車和輪胎之間就是 has-a 的關(guān)系,它們之間則適合使用組合。?

// 輪胎
class Tire {
protected:string _brand = "Michelin"; // 品牌size_t _size = 17; // 尺寸};// 汽車
class Car {
protected:string _colour = "黑色"; // 顏色string _num = "川A66688"; // 車牌號Tire _t; // 輪胎
};

?實(shí)際項(xiàng)目中,更推薦使用?組合?的方式,這樣可以做到?解耦,避免因父類的改動(dòng)而直接影響到子類

  • 公有繼承:is-a?—> 高耦合,可以直接使用父類成員
  • 組合:has-a?—> 低耦合,可以間接使用父類成員

十、繼承的總結(jié)和反思?

?1. 很多人說C++語法復(fù)雜,其實(shí)多繼承就是一個(gè)體現(xiàn)。有了多繼承,就存在菱形繼承,有了菱形繼承就有菱形虛擬繼承,底層實(shí)現(xiàn)就很復(fù)雜。所以一般不建議設(shè)計(jì)出多繼承,一定不要設(shè)計(jì)出菱形繼承。否則在復(fù)雜度及性能上都有問題。


2. 多繼承可以認(rèn)為是C++的缺陷之一,很多后來的面向?qū)ο笳Z言都沒有多繼承,如Java。


十一、共勉?

?以下就是我對 【C++】繼承 的理解,如果有不懂和發(fā)現(xiàn)問題的小伙伴,請?jiān)谠u論區(qū)說出來哦,同時(shí)我還會繼續(xù)更新對 【C++】多態(tài)?的理解,請持續(xù)關(guān)注我哦!!!????

http://www.risenshineclean.com/news/53434.html

相關(guān)文章:

  • 做國際網(wǎng)站一般做什么風(fēng)格怎么自己創(chuàng)建一個(gè)網(wǎng)頁
  • 域名綁了小程序還可以做網(wǎng)站嗎海洋seo
  • 且未縣建設(shè)局網(wǎng)站上海優(yōu)化價(jià)格
  • 中國十大電商做的好的網(wǎng)站windows優(yōu)化大師卸載不掉
  • 如何免費(fèi)制作一個(gè)自己的網(wǎng)站外匯交易平臺
  • 福建漳州網(wǎng)站建設(shè)費(fèi)用今天時(shí)政新聞熱點(diǎn)是什么
  • 手機(jī)網(wǎng)站開發(fā)注意的問題168推廣網(wǎng)
  • 怎樣做網(wǎng)站權(quán)重百度在線使用
  • 怎么用手機(jī)做網(wǎng)站編輯推廣網(wǎng)站要注意什么
  • 網(wǎng)站建設(shè)企業(yè)建站要多久優(yōu)質(zhì)的seo快速排名優(yōu)化
  • 南寧網(wǎng)站建設(shè)優(yōu)化外鏈怎么打開
  • 江蘇省實(shí)訓(xùn)基地建設(shè)網(wǎng)站鄭州網(wǎng)
  • 微營銷方案名片seo什么意思
  • 國內(nèi)視差網(wǎng)站網(wǎng)站網(wǎng)絡(luò)推廣推廣
  • 騰博會的網(wǎng)站是什么愛站網(wǎng)長尾詞挖掘工具
  • 建網(wǎng)站需要怎樣做屬于seo網(wǎng)站優(yōu)化
  • 泊頭市建設(shè)局網(wǎng)站seo入門培訓(xùn)課程
  • 國家稅務(wù)總局網(wǎng)站官網(wǎng)下載網(wǎng)絡(luò)服務(wù)器的作用
  • 求一些做里番的網(wǎng)站站長工具seo綜合查詢網(wǎng)
  • 企業(yè)內(nèi)部網(wǎng)站模板中國網(wǎng)站訪問量排行
  • 六合哪家做網(wǎng)站建設(shè)靈寶seo公司
  • wordpress 圖片連接插件福建seo
  • 做網(wǎng)站智能工具代發(fā)百度首頁排名
  • vs做網(wǎng)站怎么上做銷售有什么技巧和方法
  • 手機(jī)網(wǎng)站的做電商最好賣的十大產(chǎn)品
  • 有什么網(wǎng)站可以做投票關(guān)鍵詞搜索排名
  • 可以自己做網(wǎng)站賺錢嗎小程序制作
  • 網(wǎng)站建設(shè)donglongyun免費(fèi)自助建站模板
  • 做公司的網(wǎng)站怎么上線廈門網(wǎng)站推廣優(yōu)化哪家好
  • 北京海淀網(wǎng)站建設(shè)百度搜索數(shù)據(jù)統(tǒng)計(jì)