中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

【C++】繼承-創(chuàng)新互聯(lián)

在新吳等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都網(wǎng)站設(shè)計、網(wǎng)站制作 網(wǎng)站設(shè)計制作定制開發(fā),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),品牌網(wǎng)站設(shè)計,成都全網(wǎng)營銷推廣,成都外貿(mào)網(wǎng)站建設(shè)公司,新吳網(wǎng)站建設(shè)費用合理。

目錄

一、繼承的概念

二、被繼承成員訪問方式的變化

三、賦值兼容規(guī)則(切片)?

四、繼承中的作用域

五、子類的默認成員函數(shù)

1、父、子類中各自的成員處理方式

2、需要自己寫默認成員函數(shù)的情況

3、子類中默認成員函數(shù)的寫法

3.1父類沒有默認構(gòu)造函數(shù),需要在子類的構(gòu)造函數(shù)里補充

3.2在子類中顯式寫拷貝構(gòu)造

3.3在子類中顯式寫賦值運算符重載

3.4不需要顯式調(diào)用析構(gòu)函數(shù)

六、繼承和友元、靜態(tài)成員的關(guān)系

七、菱形繼承和菱形的虛擬繼承

1、菱形繼承

2、二義性和數(shù)據(jù)冗余

3、虛擬繼承解決二義性和數(shù)據(jù)冗余

4、virtual關(guān)鍵字解決二義性和數(shù)據(jù)冗余的方法

4.1未使用virtual關(guān)鍵字

4.2使用virtual關(guān)鍵字

4.3虛繼承+重寫問題

八、繼承和組合的區(qū)別

1、組合的使用場景

2、繼承和組合的區(qū)別


一、繼承的概念

繼承機制是面向?qū)ο蟪绦蛟O(shè)計使代碼可以復(fù)用的最重要的手段,它允許程序員在保持原有類特性的基礎(chǔ)上進行擴展,增加功能,這樣產(chǎn)生新的類,稱子類(派生類)。以前我們接觸的復(fù)用都是函數(shù)復(fù)用,繼承是類設(shè)計層次的復(fù)用。

現(xiàn)在有學(xué)生類和老師類,類中均有年齡、性別等相同的屬性,這些相同的屬性在每個類中都寫一份就會出現(xiàn)代碼的冗余??梢允褂靡粋€父類包含這些共有的成員變量和成員函數(shù),讓學(xué)生類、老師類作為子類對父類進行繼承即可。

二、被繼承成員訪問方式的變化

public繼承

protected繼承

private繼承

父類的public成員

public

protected

private

父類的protected成員

protected

protected

private

父類的private成員

子類不可見

子類不可見

子類不可見

1、父類的private成員被子類繼承后是不可見的。不可見指的是父類的private成員會被繼承到子類,但是子類無論是在類里面還是類外面,都無法對這些被繼承的私有成員進行訪問。(但是可以使用繼承的“獲取成員變量的偷家函數(shù)”對這些不可見的成員變量進行修改、訪問)

2、除了父類中的private成員,其他成員被子類繼承后最終的訪問方式取決于該成員在父類中的權(quán)限和繼承權(quán)限兩者權(quán)限較小的那個。

3、可以看出protected限定符是因為繼承才出現(xiàn)的。保護和私有在當(dāng)前類中沒有區(qū)別,但子類繼承時,父類中的私有成員子類是不可見的,而保護成員是可見的。

4、在實際中一般使用都是public繼承,因為protetced/private繼承下來的成員都只能在派生類的類里面使用,實際中擴展維護性不強。父類一般是公有和保護,不使用私有。

5、class默認私有繼承,struct默認公有繼承。但好習(xí)慣是寫明繼承方式。

struct Student : public person
{

};
struct Teacher :person//不提倡,最好寫明繼承方式
{
};
三、賦值兼容規(guī)則(切片)?
class Person
{
protected:
	string _name;
	string _sex;
	int _age;
};
class Student : public Person
{
public:
	int _num;//學(xué)號
};
int main()
{
	Person p;
	Student s;
	//父類=子類 賦值兼容/切割/切片
	p = s;//父類對象
	Person* ptr = &s;//父類的指針
	Person& ref = s;//父類的引用
	return 0;
}

1、子類對象可以賦值給父類對象、父類的指針、父類的引用。這種操作叫做賦值兼容/切割/切片,意為將子類對象中繼承于父類的成員切割下來賦值給父類對象。這不是類型轉(zhuǎn)換,是天然的賦值行為。(切片僅限公有繼承。舉例:父類為公有,子類保護或私有繼承后,成員變?yōu)楸Wo和私有,子類再切片給父類,那么被繼承的成員權(quán)限會變,所以切片僅限公有繼承)

2、只能將子類對象賦值給父類,父類對象不能給子類賦值。但是指針和引用卻可以,不過存在越界風(fēng)險。

int main()
{
	Person p;
	Student s;
	//s = (Student)p;//父類對象無法賦值給子類,強轉(zhuǎn)也不行
	Student* ptr = (Student*)& p;//類型強轉(zhuǎn),有越界風(fēng)險
	Student& ref = (Student&)p;//類型強轉(zhuǎn),有越界風(fēng)險
	return 0;
}

3、基類的指針或者引用可以通過強制類型轉(zhuǎn)換賦值給派生類的指針或者引用。但是必須是基類的指針是指向派生類對象時才是安全的。這里基類如果是多態(tài)類型,可以使用RTTI(Run-Time Type Information)的dynamic_cast 來進行識別后進行安全轉(zhuǎn)換。

四、繼承中的作用域

1、在繼承中父類和子類都有自己獨立的類域。

2、當(dāng)子類和父類中存在同名成員時,子類將會屏蔽繼承于父類的同名成員,這種情況被稱為隱藏或重定義。(子類內(nèi)部優(yōu)先使用自己類域的同名成員,外部可使用stu.Person::_name進行顯示訪問)

3、子類和父類中的同名成員函數(shù)并不構(gòu)成函數(shù)重載,因為它們所處于不同的類域,子類會隱藏父類同名函數(shù)。

4、父類和子類盡量不要使用同名成員。

五、子類的默認成員函數(shù) 1、父、子類中各自的成員處理方式

子類中有兩部分成員,一類是子類原生的成員,另一類是繼承于父類的成員。

對于原生成員,按照普通類調(diào)用默認成員函數(shù)的規(guī)則進行處理;對于繼承于父類的成員,將會調(diào)用父類中的默認成員函數(shù)進行處理。(各管各的)

2、需要自己寫默認成員函數(shù)的情況

1、父類沒有默認構(gòu)造函數(shù),需要自己顯式寫構(gòu)造。

2、子類存在淺拷貝問題,需要自己顯式寫拷貝構(gòu)造和賦值。

3、子類有資源需要釋放,需要自己寫顯式析構(gòu)。

3、子類中默認成員函數(shù)的寫法 3.1父類沒有默認構(gòu)造函數(shù),需要在子類的構(gòu)造函數(shù)里補充
class Person
{
public:
	Person(const char* name)
		: _name(name)
	{}
protected:
	string _name; // 姓名
};
class Student : public Person
{
public:
	Student(const char* name = "張三", int num = 10)
		: Person(name)//必須調(diào)用父類的構(gòu)造函數(shù)進行初始化
		, _num(num)
	{}
protected:
	int _num; //學(xué)號
};

父類有提供默認構(gòu)造函數(shù)就可以不用在子類寫了。

3.2在子類中顯式寫拷貝構(gòu)造
class Person
{
public:
	Person(const Person& p)//形參:引用切片對象
		: _name(p._name)
	{}
protected:
	string _name; // 姓名
};
class Student : public Person
{
public:
	Student(const Student& s)
		:Person(s)//切片
		,_num(s._num)
	{}
protected:
	int _num; //學(xué)號
};

利用切片傳入父類對象構(gòu)造父類。Student s1(s2),在初始化列表中,利用s2中的父類成員去拷貝構(gòu)造s1中的父類成員。

3.3在子類中顯式寫賦值運算符重載
class Person
{
public:
	Person& operator=(const Person& p)
	{
		if (this != &p)
			_name = p._name;
		return *this;
	}
protected:
	string _name; // 姓名
};
class Student : public Person
{
public:
	Student& operator=(const Student& s)
	{
		if (this != &s)//防止自己給自己賦值
		{
			Person::operator=(s);//切片傳入父類賦值運算符重載中
			//根據(jù)子類成員進行深淺拷貝
			_num = s._num;
		}
		return *this;
	}
protected:
	int _num; //學(xué)號
};

在子類賦值運算符重載中調(diào)用父類賦值運算符重載,通過切片,完成父類成員的賦值。

3.4不需要顯式調(diào)用析構(gòu)函數(shù)

錯誤代碼:

~Student()
{
    Person::~Person();
}
//子類析構(gòu)函數(shù)結(jié)束后會調(diào)用一次父類的析構(gòu)函數(shù)

~Person前必須加類域Person。因為析構(gòu)函數(shù)的名字會被編譯器統(tǒng)一處理為destructor(),子類的析構(gòu)函數(shù)和父類的析構(gòu)函數(shù)之間構(gòu)成隱藏,所以這里需要寫明類域。

但是,我們并不需要顯式調(diào)用父類的析構(gòu)函數(shù),因為出了子類析構(gòu)函數(shù)的作用域,編譯器會自動調(diào)用父類的析構(gòu)。手動調(diào)用父類析構(gòu)將會造成重復(fù)析構(gòu)。

六、繼承和友元、靜態(tài)成員的關(guān)系

1、友元關(guān)系不能被繼承

2、父類中的靜態(tài)成員也會被繼承,但是整個繼承關(guān)系中共用這個靜態(tài)成員

七、菱形繼承和菱形的虛擬繼承 1、菱形繼承

從上圖可以看出,Assistant中會有兩份Person成員,調(diào)用時存在二義性和數(shù)據(jù)冗余。

2、二義性和數(shù)據(jù)冗余
int main()
{
	// 這樣會有二義性無法明確知道訪問的是哪一個
	Assistant a;
	//a._name = "peter";//不能這么寫,因為a中有兩個_name成員,需要指定類域
	// 需要顯示指定訪問哪個父類的成員可以解決二義性問題,但是數(shù)據(jù)冗余問題無法解決
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
	return 0;
}

由于Assistant中有兩個_name成員,直接調(diào)用存在二義性,需要在成員之前指定類域。

_name這個成員變量有兩個問題不大,畢竟一個人可以叫蔣靈瑜,在其他場合也可以叫小蔣。但如果這個成員變量是一個int _arr[50000]的數(shù)組呢,一個類中同時有兩份這么大的數(shù)組,將會導(dǎo)致數(shù)據(jù)冗余。

3、虛擬繼承解決二義性和數(shù)據(jù)冗余

產(chǎn)生二義性和數(shù)據(jù)冗余的本質(zhì)就是子類繼承了多份相同成員。

解決方法是在“腰部”類增加virtual關(guān)鍵字。

class Person
{
public:
	string _name; // 姓名
};
class Student :virtual public Person
{
protected:
	int _num; //學(xué)號
};
class Teacher : virtual public Person
{
protected:
	int _id; // 職工編號
};
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修課程
};
4、virtual關(guān)鍵字解決二義性和數(shù)據(jù)冗余的方法

先來一段菱形繼承的代碼,_a、_b、_c、_d分別是類A、類B、類C、類D中的原生成員。

class A
{
public:
	int _a;
};
 class B : public A
//class B : virtual public A
{
public:
	int _b;
};
 class C : public A
//class C : virtual public A
{
public:
	int _c;
};
class D : public B, public C
{
public:
	int _d;
};
int main()
{
	D d;
	d.B::_a = 1;
	d._b = 2;
	d.C::_a = 3;
	d._c = 4;
	d._d = 5;
	return 0;
}
4.1未使用virtual關(guān)鍵字

通過調(diào)用內(nèi)存,會發(fā)現(xiàn)對象d中存在兩份的_a,存在二義性和數(shù)據(jù)冗余。

4.2使用virtual關(guān)鍵字

當(dāng)使用了虛擬繼承,通過調(diào)用內(nèi)存,發(fā)現(xiàn)對象d中僅有一份_a,但是繼承于B類和C類的_b和_c上方多了一串地址,再次要通過內(nèi)存查找這串地址,發(fā)現(xiàn)這串地址之后的位置存放一個數(shù)字0x14,這個數(shù)字就是繼承于B的成員到_a的偏移量,通過這個偏移量,對象d便能到d.B::_a。這樣就解決了菱形繼承成員冗余的問題。

這里的A叫做虛基類,在對象d中,將虛基類的成員放到一個公共的位置,繼承的B、C類需要找到A的成員,通過虛基表中的偏移量進行計算。我們看到虛基表中第一行還空置了4個字節(jié),這塊空間存放的也是一個偏移量,它用于尋找d對象中的虛函數(shù)指針表。

實際使用時,盡量不要使用用菱形繼承,因為它本質(zhì)就是C++設(shè)計的一個坑!

4.3虛繼承+重寫問題

1、如果A類中還存在一個虛函數(shù),那么對象d會在_a后面存放虛函數(shù)指針表;

2、如果A類中存在一個虛函數(shù),并且B、C類均對這個虛函數(shù)進行了重寫,那么D類中必須對這個函數(shù)進行重寫,否則將會發(fā)生虛函數(shù)表重命名的問題。

八、繼承和組合的區(qū)別

組合也是一種類復(fù)用的手段。

1、組合的使用場景

適用組合的代碼:輪胎和車的關(guān)系

class Tire
{
protected:
   string _brand = "Michelin"; ?// 品牌
   size_t _size = 18; ? ? ? ? // 尺寸
};
 ? 
class Car{
protected:
    string _colour = "白色"; // 顏色
    string _num = "xxxxx"; // 車牌號
    Tire _t; // 輪胎
}; ?
2、繼承和組合的區(qū)別

public繼承是一種is-a的關(guān)系,每個子類對象都是一個父類對象,例如“學(xué)生”是“人”(子類學(xué)生,父類人)

組合是一種has-a的關(guān)系,B組合了A,每個B對象中都有一個A,例如“車”包含“輪胎”

如果兩個類既可以是is-a,又可以是has-a的關(guān)系,那么優(yōu)先使用組合。

繼承是一種白盒復(fù)用,父類內(nèi)部的細節(jié)對子類可見,破壞了封裝。子類將會繼承父類的公有和保護成員,一旦父類修改了這些成員的實現(xiàn),將會影響子類的功能。子類和父類之間的依賴關(guān)系強,耦合度高。

組合是一種黑盒復(fù)用,父類內(nèi)部的細節(jié)對子類不可見,子類僅可使用父類的公有成員,只要父類的公有成員的實現(xiàn)細節(jié)不變,子類影響較小。父子之間沒有很強的依賴關(guān)系,耦合度較低。

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧

本文標(biāo)題:【C++】繼承-創(chuàng)新互聯(lián)
網(wǎng)頁地址:http://www.rwnh.cn/article8/jdgop.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信公眾號、動態(tài)網(wǎng)站軟件開發(fā)、網(wǎng)站收錄用戶體驗、外貿(mào)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)頁設(shè)計公司
蒙自县| 安远县| 阳泉市| 元氏县| 宁远县| 永春县| 舞阳县| 京山县| 苏州市| 枣庄市| 景宁| 五莲县| 方城县| 望奎县| 镇坪县| 彭山县| 衡南县| 左贡县| 麻栗坡县| 高碑店市| 桑植县| 辉县市| 赤峰市| 富顺县| 嘉荫县| 辽中县| 乐至县| 上犹县| 融水| 莆田市| 会东县| 吉隆县| 山丹县| 本溪市| 定边县| 黑河市| 涞源县| 库车县| 嘉善县| 邹平县| 温宿县|