真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

C++知識(shí)點(diǎn)--繼承-創(chuàng)新互聯(lián)

C++知識(shí)點(diǎn) – 繼承

公司主營業(yè)務(wù):網(wǎng)站設(shè)計(jì)制作、成都做網(wǎng)站、移動(dòng)網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)公司是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)公司推出龍?zhí)睹赓M(fèi)做網(wǎng)站回饋大家。文章目錄
  • C++知識(shí)點(diǎn) -- 繼承
  • 一、概念及定義
    • 1.概念
    • 2.定義
  • 二、繼承中的作用域
  • 三、基類和派生類的對象賦值轉(zhuǎn)換
  • 四、派生類的默認(rèn)成員函數(shù)
    • 1.默認(rèn)構(gòu)造函數(shù)
    • 2.默認(rèn)析構(gòu)函數(shù)
    • 3.默認(rèn)拷貝構(gòu)造函數(shù)
    • 4.默認(rèn)賦值重載函數(shù)
  • 五、繼承與友元
  • 六、繼承與靜態(tài)成員
  • 七、多繼承、菱形繼承與虛擬繼承
    • 1.單繼承:一個(gè)子類只有一個(gè)父類
    • 2.多繼承:一個(gè)子類有兩個(gè)或以上直接父類
    • 3.菱形繼承:
    • 4.虛擬繼承
    • 5.虛擬繼承解決數(shù)據(jù)冗余和二義性問題的原理
  • 八、如何定義一個(gè)不能被繼承的類


一、概念及定義 1.概念

繼承是面向?qū)ο蟮某绦蛟O(shè)計(jì)能夠?qū)崿F(xiàn)類的代碼復(fù)用的重要手段,它允許程序員在保留原有類的特性的基礎(chǔ)上進(jìn)行擴(kuò)展。
我們將共有的數(shù)據(jù)和方法提取到一個(gè)類中,這個(gè)類叫做父類/基類;
然后在此基礎(chǔ)上用需要擴(kuò)展的數(shù)據(jù)和方法產(chǎn)生新的類,并且繼承父類,這些類叫做子類/派生類。
代碼如下:

class Person
{public:
	void print()
	{cout<< "name: "<< _name<< endl;
		cout<< "age: "<< _age<< endl;
	}
protected:
	string _name;
	int _age;
};

class Student : public Person
{protected:
	int _stuid;
};

class Teacher : public Person
{protected:
	int _Jobid;
};

int main()
{Student s1;
	Teacher t1;
	s1.print();
	t1.print();

	return 0;
}

從以上代碼我們可以看出,父類是Person,Student和Teacher兩個(gè)類都繼承了Person,在創(chuàng)建對象后,都能夠通過對象訪問Person中的公有成員函數(shù)。

2.定義

在上面的代碼Student類中,Student是子類,:后面的public是繼承方式,Person是父類。
繼承方式與訪問限定符一樣,都有三種:public、protected和private,繼承方式與訪問限定符之間的關(guān)系如下:
在這里插入圖片描述
1.基類的private成員無論什么繼承方式都是不可見的;
不可見的意思是:隱身,在類作用域里面和類外面都不能訪問,例如:

class Person
{public:
	void print()
	{cout<< "name: "<< _name<< endl;
		cout<< "age: "<< _age<< endl;
	}
//protected:
private:
	string _name;
	int _age;
};

class Student : public Person
{public:
	void print_stu()
	{cout<< _name<< _age<< _stuid<< endl;
	}
protected:
	int _stuid;
};

Student以public繼承了Person類,但是Person類中的成員變量都是private的,即使繼承到Student類中,也不能夠訪問,而且在類的外面也不能訪問。

相對而言,protect和private的意思是,在類作用域里面可以訪問,在類外面不能訪問:

class Person
{public:
	void print()
	{cout<< "name: "<< _name<< endl;
		cout<< "age: "<< _age<< endl;
	}
protected:
//private:
	string _name;
	int _age;
};

class Student : public Person
{public:
	void print_stu()
	{cout<< _name<< _age<< _stuid<< endl;
	}
protected:
	int _stuid;
};

將Person中的成員變量限定改為protected,公有繼承到Student類中,就能在子類里訪問_name和_age了,但是在類外面依舊不能訪問。

2.實(shí)際上最常用的是以下的兩種繼承關(guān)系:
在這里插入圖片描述
3.除了父類的私有成員在子類中都是不可見的,其他基類成員在子類中的訪問方式 == Min(成員在父類的訪問限定符, 繼承方式),public >protected >private;

4.使用關(guān)鍵字struct的默認(rèn)繼承方式是public,使用class的默認(rèn)繼承方式是private,最好顯式寫出繼承方式;

class Student1 : Person//默認(rèn)繼承方式是private
{protected:
	int _stuid;
};

struct Student2 : Person//默認(rèn)繼承方式是public
{int _stuid;
};

注:struct的默認(rèn)訪問限定符是public,class的默認(rèn)訪問限定符是private;

5.private成員的意義:不想被子類繼承的成員,可以設(shè)計(jì)成私有;
如果父類中想給子類復(fù)用,但又不想直接暴露在外面的成員,就設(shè)計(jì)成protected;

二、繼承中的作用域

1.在繼承中,父類和子類都有獨(dú)立的作用域;

2.若父類和子類中有同名的成員,子類成員將屏蔽父類對同名成員的直接訪問,這種情況叫做隱藏,也叫重定義。
若想訪問父類中的同名成員,可以在前面加上父類的作用域,如 Person::print();

class Person
{public:
	void print()
	{cout<< "name: "<< _name<< endl;
		cout<< "age: "<< _age<< endl;
	}
protected:
	string _name;
	int _age;
	int _num = 111;
};

class Student : public Person
{public:
	void print_stu()
	{cout<< _num<< endl;
		cout	}
protected:
	int _stuid;
	int _num = 999;
};
int main()
{Student s1;
	s1.print_stu();

	return 0;
}

上述代碼的運(yùn)行結(jié)果是:
在這里插入圖片描述

第一行的結(jié)果999就是由于子類和父類有同名成員,父類中的就被隱藏了,默認(rèn)訪問的是子類的_num;
第二行的結(jié)果111就是在_num前加了Person的作用域限定,訪問的就是父類中的成員了。

3.如果是同名的成員函數(shù),只需要函數(shù)名相同就構(gòu)成隱藏,參數(shù)不相同也是隱藏,這里需要和函數(shù)的重載進(jìn)行區(qū)分;
函數(shù)的隱藏是有繼承關(guān)系的父類和子類中有同名的成員函數(shù),這兩個(gè)函數(shù)有不同的作用域,且只需函數(shù)名相同;
函數(shù)的重載是在同一作用域下,兩個(gè)同名且參數(shù)不一樣的函數(shù),才構(gòu)成重載;

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

1.派生類對象可以賦值給基類的對象、基類的指針、基類的引用,是將派生類中基類的那部分切片后,再賦值過去;
2.基類對象不可賦值給派生類對象;
3.基類的指針或引用可以通過強(qiáng)制類型轉(zhuǎn)換賦值給派生類的指針或引用;
在這里插入圖片描述

int main()
{Student s1;
	Teacher t1;

	Person p1 = s1;
	Person* pp = &s1;
	Person& rp = s1;
	
	Student* ps = (Student*)pp;//父類指針可以通過強(qiáng)制類型轉(zhuǎn)換賦值給子類指針
	return 0;
}

在這里插入圖片描述
注:父類的指針指向的是子類中切片的父類的那部分;

例題:以下代碼中p1,p2,p3的關(guān)系是:
在這里插入圖片描述
創(chuàng)建了一個(gè)子類對象d,父類Base1的指針p1指向d,父類Base2的指針p2指向d,p3是子類Derive的指針;
d對象中包含繼承的父類對象Base1和Base2還有_d,p1指向的是d中切片的Base1的那部分,p2指向的是d中切片的Base2的那部分,所以p1 != p2;
p3指向的是子類對象d,指向的是最開始的部分,因此p3 == p1;
正確答案:C
在這里插入圖片描述

注:
在這里插入圖片描述
先繼承的類放在子類對象的前面部分;
在這里插入圖片描述
由于棧是從高地址向低地址增長的,而對象是按照一個(gè)整體去創(chuàng)建的,因此子類對象的位置如上圖所示,類對象在棧里面是倒著放的,因此p2 >p1;

四、派生類的默認(rèn)成員函數(shù) 1.默認(rèn)構(gòu)造函數(shù)

子類默認(rèn)生成的構(gòu)造函數(shù)會(huì)完成如下操作:
1.對于自己特有的成員,內(nèi)置類型不做處理,自定義類型調(diào)用其構(gòu)造函數(shù);
2.對于繼承父類的成員,必須調(diào)用父類的構(gòu)造函數(shù)初始化。

如果要顯式寫構(gòu)造函數(shù),代碼如下:

class Person
{public:
	Person(const char* name)
		: _name(name)
	{cout<< "Person(const char* name)"<< endl;
	}

protected:
	string _name;
};

class Student : public Person
{public:
	Student(const char* name = "Peter", int id = 1000000)
		: _name(name)
		, _stuid(id)
	{cout<< "Student(const char* name, int id)"<< endl;
	}

protected:
	int _stuid;
};

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

當(dāng)子類用初始化列表初始化父類的成員時(shí),上面的寫法會(huì)報(bào)錯(cuò):
在這里插入圖片描述
這是因?yàn)樽宇愂菍⒏割惪醋饕粋€(gè)整體去初始化的,父類成員必須調(diào)用父類的構(gòu)造函數(shù),不能再子類中單獨(dú)初始化;
正確方式入下:

class Student : public Person
{public:
	Student(const char* name, int id)
		: Person(name)         //注意這種特殊寫法!
		, _stuid(id)
	{cout<< "Student(const char* name, int id)"<< endl;
	}

這里類似于一個(gè)匿名對象,但不是,注意這種特殊寫法!

2.默認(rèn)析構(gòu)函數(shù)

子類默認(rèn)生成的析構(gòu)函數(shù)與默認(rèn)構(gòu)造函數(shù)類似:
1.對于自己特有的成員,內(nèi)置類型不做處理,自定義類型調(diào)用其析構(gòu)函數(shù);
2.對于繼承父類的成員,必須調(diào)用父類的析構(gòu)函數(shù)。

如果要顯式寫析構(gòu)函數(shù),代碼如下:

class Person
{public:
	~Person()
	{cout<< "~Person()"<< endl;
	}

protected:
	string _name;
};

class Student : public Person
{public:
	~Student()
	{~Person();     //在子類的析構(gòu)中調(diào)用父類的析構(gòu)
	}

protected:
	int _stuid;
};

上面這種寫法時(shí)會(huì)報(bào)錯(cuò)的,因?yàn)橛捎诙鄳B(tài)的需要,析構(gòu)函數(shù)的名字會(huì)被統(tǒng)一處理成destructor(),子類的析構(gòu)和父類的析構(gòu)構(gòu)成了隱藏,只要加上作用域就行了:

~Student()
	{Person::~Person();
	}

如果子類的析構(gòu)中顯式調(diào)用了父類的析構(gòu)(如上面代碼所示),那么會(huì)出現(xiàn)以下情況:
在這里插入圖片描述
父類的析構(gòu)被調(diào)用了兩次,重復(fù)析構(gòu)了,造成這種現(xiàn)象的原因是:
每個(gè)子類的析構(gòu)函數(shù)后面,都會(huì)默認(rèn)調(diào)用父類的析構(gòu)函數(shù),這樣才能保證先析構(gòu)子類,再析構(gòu)父類,所以子類的析構(gòu)函數(shù)中不去顯式調(diào)用父類的析構(gòu)函數(shù)才是正確的。

~Student()
	{cout<< "~Student()"<< endl;
	}

注:子類構(gòu)造時(shí),先構(gòu)造父類,再構(gòu)造子類的成員;
子類析構(gòu)時(shí),先析構(gòu)子類成員,再析構(gòu)父類。

3.默認(rèn)拷貝構(gòu)造函數(shù)

子類默認(rèn)拷貝構(gòu)造會(huì)進(jìn)行如下操作:
1.對于自己特有的成員,內(nèi)置類型會(huì)進(jìn)行淺拷貝,自定義類型會(huì)調(diào)用其拷貝構(gòu)造;
2.對于繼承的父類成員,必須調(diào)用父類的拷貝構(gòu)造。

如果子類中顯式寫拷貝構(gòu)造,代碼如下:

class Person
{public:
	Person(const Person& p)
		: _name(p._name)
	{cout<< "Person(const Person& p)"<< endl;
	}
protected:
	string _name;
};

class Student : public Person
{public:
	Student(const Student& s)
		: Person(s)				//運(yùn)用了切片的原理
		, _stuid(s._stuid)
	{cout<< "Student(const Student& s)"<< endl;
	}

protected:
	int _stuid;
};

在子類的拷貝構(gòu)造中,沒法將子類中父類的成員直接拿出來進(jìn)行賦值,這時(shí)就將子類直接賦值給父類,構(gòu)成賦值切片。

4.默認(rèn)賦值重載函數(shù)

編譯器默認(rèn)生成的子類賦值重載函數(shù)進(jìn)行的操作如下:
1.對于自己特有的成員,內(nèi)置類型會(huì)進(jìn)行淺拷貝,自定義類型會(huì)調(diào)用其賦值重載;
2.對于繼承的父類成員,必須調(diào)用父類的賦值重載。

如果子類顯式寫賦值重載,代碼如下:

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)
		{	operator=(s);			//父類的成員調(diào)用父類的賦值重載
			_stuid = s._stuid;
		}
		return *this;
	}
protected:
	int _stuid;
};

上面的寫法是會(huì)報(bào)錯(cuò)的,因?yàn)樽宇惖膐perator=()和繼承父類的operator=()構(gòu)成了隱藏,這時(shí),只要指定了父類的作用域就可以了:

Student& operator=(const Student& s)
	{if (this != &s)
		{	Person::operator=(s);			//父類的成員調(diào)用父類的賦值重載
			_stuid = s._stuid;
		}
		return *this;
	}
五、繼承與友元

友元關(guān)系不能繼承?。?!
也就是說,父類的友元函數(shù)不能訪問子類的私有和保護(hù)成員。

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

父類定義了一個(gè)static靜態(tài)成員,那么整個(gè)繼承體系中就只有這一個(gè)成員,無論派生出多少子類,都只有一個(gè)static成員的實(shí)例。

七、多繼承、菱形繼承與虛擬繼承 1.單繼承:一個(gè)子類只有一個(gè)父類

在這里插入圖片描述

2.多繼承:一個(gè)子類有兩個(gè)或以上直接父類

在這里插入圖片描述

3.菱形繼承:

在這里插入圖片描述
代碼如下:

class Person
{protected:
	string _name;
};

class Student : public Person
{protected:
	int _stuid;
};

class Teacher : public Person
{protected:
	int _Jobid;
};

class Assistant :public Student, public Teacher
{protected:
	string _majorCourse;
};

菱形繼承是由多繼承引發(fā)的一個(gè)問題:
在這里插入圖片描述
從上圖可以看出,菱形繼承有數(shù)據(jù)冗余和二義性的問題,在Assistant對象中Person成員會(huì)有兩份,而且在Assistant對象訪問Person的成員時(shí)們也會(huì)出現(xiàn)二義性問題,無法明確知道訪問的是哪一個(gè)成員。

int main()
{Assistant a1;
	a1._name = "Jack";    //編譯器無法知道訪問的是那個(gè)父類的_name
	
	a1.Student::_name = "Jack";  //可以通過指定作用域來解決二義性問題,但是數(shù)據(jù)冗余問題無法解決
	a1.Teacher::_name = "Bob";
	
	return 0;
}
4.虛擬繼承

虛擬繼承可以解決菱形繼承的數(shù)據(jù)冗余問題和二義性問題,在Student和Teacher繼承Person時(shí),使用虛擬繼承,代碼如下:

class Person
{protected:
	string _name;
};

class Student : virtual public Person    //虛擬繼承
{protected:
	int _stuid;
};

class Teacher : virtual public Person
{protected:
	int _Jobid;
};

class Assistant :public Student, public Teacher
{protected:
	string _majorCourse;
};

這樣在實(shí)例化Assistant對象時(shí),對象中只會(huì)有一組Person的成員,不會(huì)出現(xiàn)數(shù)據(jù)冗余和二義性問題。

5.虛擬繼承解決數(shù)據(jù)冗余和二義性問題的原理

給出一個(gè)簡化的菱形繼承,代碼如下:

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.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;

	return 0;
}

下圖是菱形繼承的對象成員模型,可以看到有數(shù)據(jù)冗余,A的成員有兩份:
在這里插入圖片描述
如果將B和C對A的繼承改成虛擬繼承,其對象成員模型如下:
在這里插入圖片描述
不存在數(shù)據(jù)冗余了,A的成員只有一份,B和C中原本存放A成員的地方,現(xiàn)在存放了一個(gè)地址,叫做虛基表指針,指向的就是虛基表,虛基表中存放的是當(dāng)前對象與虛繼承的父類對象之間的偏移量,通過偏移量來找到A。

在普通繼承中不要使用虛繼承,因?yàn)闀?huì)開辟虛基表,造成空間浪費(fèi)。

八、如何定義一個(gè)不能被繼承的類

在類名后加一個(gè)關(guān)鍵詞:final,表示當(dāng)前類為最終類,無法被繼承。代碼如下:

class A final
{public:
	int _a;
};

在這里插入圖片描述

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


網(wǎng)頁標(biāo)題:C++知識(shí)點(diǎn)--繼承-創(chuàng)新互聯(lián)
轉(zhuǎn)載注明:http://weahome.cn/article/digiio.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部