欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文帶你吃透C++繼承

 更新時(shí)間:2023年06月06日 09:44:57   作者:披星戴月的賈維斯  
繼承是C++語(yǔ)言的一個(gè)重要特性,繼承使得軟件(代碼)復(fù)用變得簡(jiǎn)單、易行,可以通過(guò)繼承復(fù)用已有的程序資源,縮短軟件開(kāi)發(fā)的周期,本就帶大家吃透C++繼承,需要的朋友可以參考下

??1、繼承的概念及定義

??1.1繼承的概念

繼承(inheritance)機(jī)制是面向?qū)ο蟪绦蛟O(shè)計(jì)使代碼可以復(fù)用的最重要的手段,它允許程序員在保持原有類特性的基礎(chǔ)上進(jìn)行擴(kuò)展,增加功能,這樣產(chǎn)生新的類,稱派生類。繼承呈現(xiàn)了面向?qū)ο蟪绦蛟O(shè)計(jì)的層次結(jié)構(gòu),體現(xiàn)了由簡(jiǎn)單到復(fù)雜的認(rèn)知過(guò)程。以前我們接觸的復(fù)用都是函數(shù)復(fù)用,繼承是類設(shè)計(jì)層次的復(fù)用。

繼承&組合(也稱合成): 是C++實(shí)現(xiàn)代碼重用的2種主要方法。

??1.2圖解繼承例子

在這里插入圖片描述

代碼示例:

class Person
{
public:
	void Print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
	string _name = "jojo"; // 姓名
	int _age = 18; // 年齡
};
// 繼承后父類的Person的成員(成員函數(shù)+成員變量)都會(huì)變成子類的一部分。這里體現(xiàn)出了Student和
//Teacher復(fù)用了Person的成員。下面我們使用監(jiān)視窗口查看Student和Teacher對(duì)象,可以看到變量的復(fù)用。
//調(diào)用Print可以看到成員函數(shù)的復(fù)用。
class Student : public Person
{
private:
	int _stuid; // 學(xué)號(hào)
};
class Teacher : public Person
{
protected:
	int _jobId; // 工號(hào)
};
int main()
{
	Student s;
	Teacher t;
	s.Print();
	t.Print();
	return 0;
}

??1.3 繼承的語(yǔ)法形式

在這里插入圖片描述

??1.3.1繼承基類成員訪問(wèn)方式的變化

在這里插入圖片描述

總結(jié):

  • 基類private成員在派生類中無(wú)論以什么方式繼承都是不可見(jiàn)的。這里的不可見(jiàn)是指基類的私有成員還是被繼承到了派生類對(duì)象中,但是語(yǔ)法上限制派生類對(duì)象不管在類里面還是類外面都不能去訪問(wèn)它。
  • 基類private成員在派生類中是不能被訪問(wèn),如果基類成員不想在類外直接被訪問(wèn),但需要在派生類中能訪問(wèn),就定義為protected。可以看出保護(hù)成員限定符是因繼承才出現(xiàn)的
  • 使用關(guān)鍵字class時(shí)默認(rèn)的繼承方式是private,使用struct時(shí)默認(rèn)的繼承方式是public,不過(guò)最好顯示的寫(xiě)出繼承方式。
  • 在實(shí)際運(yùn)用中一般使用都是public繼承,幾乎很少使用protetced/private繼承。
  • 如果不想讓一個(gè)類作為其它類的基類,可以用final關(guān)鍵字阻止它被繼承。

??2、 基類&派生類的關(guān)系

通過(guò)繼承,派生類擁有了基類的數(shù)據(jù)成員和函數(shù)成員。

??派生類在基類的基礎(chǔ)上可實(shí)施以下改變??:

  • 增加新的成員(數(shù)據(jù)+函數(shù))。
  • 重載(overload)基類的函數(shù)成員。
  • 重定義(override,覆蓋)基類已有的函數(shù)成員。
  • 改變基類成員在派生類中的訪問(wèn)屬性。

??派生類不能繼承基類的以下內(nèi)容??:

1)析構(gòu)函數(shù)。
2) 基類的友元函數(shù)。
3)靜態(tài)成員(數(shù)據(jù)+函數(shù))
4)針對(duì)基類定義的一些特殊運(yùn)算符,如new等。

在這里插入圖片描述

注意:

派生類繼承了基類的所有成員,但派生類能夠直接訪問(wèn)從基類繼承來(lái)的公有和保護(hù)成員,且只能通過(guò)這兩類成員訪問(wèn)從基類繼承來(lái)的私有成員。

??3、基類和派生類對(duì)象賦值兼容轉(zhuǎn)換

  • 派生類對(duì)象 可以賦值給 基類的對(duì)象 / 基類的指針 / 基類的引用。這里有個(gè)形象的說(shuō)法叫切片或者切割。寓意把派生類中父類那部分切來(lái)賦值過(guò)去。
  • 基類對(duì)象不能賦值給派生類對(duì)象。
  • 基類的指針可以通過(guò)強(qiáng)制類型轉(zhuǎn)換賦值給派生類的指針。但是必須是基類的指針是指向派生類對(duì)象時(shí)才是安全的。這里基類如果是多態(tài)類型,可以使用RTTI(Run-Time Type Information)的dynamic_cast 來(lái)進(jìn)行識(shí)別后進(jìn)行安全轉(zhuǎn)換。
  • 基類和派生類的賦值轉(zhuǎn)換的基礎(chǔ)是建立在公有繼承的基礎(chǔ)上的
  • 引用類型通常是用.來(lái)訪問(wèn)的,指針類型的訪問(wèn)就要通過(guò)->

在這里插入圖片描述

代碼示例:

class Person
{
protected:
	string _name; // 姓名
	string _sex; // 性別
	int _age; // 年齡
};
class Student : public Person
{
public:
	int _No; // 學(xué)號(hào)
};
int main()
{
	Student sobj;
	// 1.子類對(duì)象可以賦值給父類對(duì)象/指針/引用
	Person pobj = sobj;
	Person* pp = &sobj;
	Person& rp = sobj;
	//2.基類對(duì)象不能賦值給派生類對(duì)象
	//sobj = pobj;這邊會(huì)報(bào)錯(cuò)
	// 3.基類的指針可以通過(guò)強(qiáng)制類型轉(zhuǎn)換賦值給派生類的指針
	pp = &sobj;
	Student * ps1 = (Student*)pp; // 這種情況轉(zhuǎn)換時(shí)可以的。
	ps1->_No = 10;
	pp = &pobj;
	Student* ps2 = (Student*)pp; // 這種情況轉(zhuǎn)換時(shí)雖然可以,但是會(huì)存在越界訪問(wèn)的問(wèn)題
	ps2->_No = 10;
}

??4、繼承中的作用域

  • 在繼承體系中基類和派生類都有獨(dú)立的作用域。
  • 子類和父類中有同名成員,子類成員將屏蔽父類對(duì)同名成員的直接訪問(wèn),這種情況叫隱藏,也叫重定義。(在子類成員函數(shù)中,可以使用 基類::基類成員 顯示訪問(wèn))
  • 需要注意的是如果是成員函數(shù)的隱藏,只需要函數(shù)名相同就構(gòu)成隱藏。
  • 注意在實(shí)際中在繼承體系里面最好不要定義同名的成員。

隱藏關(guān)系代碼舉例:

class Base { //基類
public:
	Base(int a = 0) { k = a; }
	int k;      //為演示,將數(shù)據(jù)成員聲明為公有
	void fn1() { cout << "Base::fn1()" << endl; }
	void fn2() { cout << "Base::fn2()" << endl; }
};
class Derived : public Base { //派生類
public:
	void fn1() { cout << "Devired::fn1()" << endl; }//構(gòu)成隱藏
	void fn2() { cout << "Devired::fn2()" << endl; }
	//如果我們想訪問(wèn)基類的fn1函數(shù)可以指定作用域
};
int main() {
	Derived d;
	cout << "k=" << d.k << endl;
	d.fn1();
	d.fn2();
	d.Base::fn1();
}

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

  • 派生類的構(gòu)造函數(shù)必須調(diào)用基類的構(gòu)造函數(shù)初始化基類的那一部分成員。如果基類沒(méi)有默認(rèn)的構(gòu)造函數(shù),則必須在派生類構(gòu)造函數(shù)的初始化列表階段顯示調(diào)用.
  • 派生類的拷貝構(gòu)造函數(shù)必須調(diào)用基類的拷貝構(gòu)造完成基類的拷貝初始化
  • 派生類的operator=必須要調(diào)用基類的operator=完成基類的復(fù)制
  • 派生類的析構(gòu)函數(shù)會(huì)在被調(diào)用完成后自動(dòng)調(diào)用基類的析構(gòu)函數(shù)清理基類成員。因?yàn)檫@樣才能保證派生類對(duì)象先清理派生類成員再清理基類成員的順序。
  • 派生類對(duì)象初始化先調(diào)用基類構(gòu)造再調(diào)派生類構(gòu)造
  • 派生類對(duì)象析構(gòu)清理先調(diào)用派生類析構(gòu)再調(diào)基類的析構(gòu)。

在這里插入圖片描述

已知父類代碼Person如下:

class Person
{
public:
	Person(const char* name = "peter")//有參的構(gòu)造函數(shù)
		: _name(name)
	{
		cout << "Person()" << endl;
	}
	Person(const Person& p)//Person給拷貝構(gòu)造函數(shù)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}
	Person& operator=(const Person& p)//operator重載=
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;
		return *this;
	}
	~Person()//析構(gòu)函數(shù)
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};

如何實(shí)現(xiàn)子類的拷貝構(gòu)造?派生類的拷貝構(gòu)造函數(shù)必須調(diào)用基類的拷貝構(gòu)造完成基類的拷貝初始化
子類有一個(gè)Int 類型的成員_num。

在這里插入圖片描述

子類完整代碼示例:

class Student : public Person
{
public:
	Student(const char* name, int num)
		: Person(name)
		, _num(num)
	{
		cout << "Student()" << endl;
	}
	Student(const Student& s)
		: Person(s)
		, _num(s._num)
	{
		cout << "Student(const Student& s)" << endl;
	}
	Student& operator = (const Student& s)
	{
		cout << "Student& operator= (const Student& s)" << endl;
		if (this != &s)
		{
			Person::operator =(s);
			_num = s._num;
		}
		return *this;
	}
	~Student()
	{
		cout << "~Student()" << endl;
	}
protected:
	int _num; //學(xué)號(hào)
};
  • 子類里面要不要顯示調(diào)用父類析構(gòu)函數(shù)的問(wèn)題?

答案是不用,因?yàn)楦缸宇惖奈鰳?gòu)函數(shù)構(gòu)成隱藏關(guān)系,子類會(huì)自動(dòng)調(diào)用父類的析構(gòu)函數(shù)。
析構(gòu)名稱會(huì)被統(tǒng)一處理成destructor()

  • 如何設(shè)計(jì)一個(gè)不能被繼承的類?

答案:構(gòu)造函數(shù)私有,這樣子類就無(wú)法初始化基類對(duì)象,從而不能被繼承。

  • 派生類對(duì)象的構(gòu)造和析構(gòu)順序

派生類對(duì)象的構(gòu)造順序:
步驟1:先構(gòu)造基類;
步驟2:再構(gòu)造對(duì)象成員;
步驟3:最后構(gòu)造派生類自身;

  • 派生類什么時(shí)候可以不定義構(gòu)造函數(shù)?

當(dāng)基類 和 所有 對(duì)象成員具有無(wú)參構(gòu)造函數(shù)時(shí)!
無(wú)參構(gòu)造函數(shù)細(xì)分為3種情況: (1)沒(méi)有定義任何構(gòu)造函數(shù); (2)具有[重定義的]無(wú)參構(gòu)造函數(shù); (3)具有缺省參數(shù)的構(gòu)造函數(shù)。

  • 派生類什么時(shí)候“必須”定義構(gòu)造函數(shù)?

答案:當(dāng)基類或?qū)ο蟪蓡T所屬類只含有帶參數(shù)的構(gòu)造函數(shù)時(shí)。即使派生類本身沒(méi)有數(shù)據(jù)成員要初始化,它也必須定義構(gòu)造函數(shù)!以“構(gòu)造函數(shù)初始化列表”的方式向基類和對(duì)象成員的構(gòu)造函數(shù)傳遞參數(shù),以實(shí)現(xiàn)基類子對(duì)象和對(duì)象成員的初始化。

??6、繼承與友元、靜態(tài)函數(shù)

友元關(guān)系不能繼承,也就是說(shuō)基類友元不能訪問(wèn)子類私有和保護(hù)成員

**如果一個(gè)類繼承了其它類,則它聲明的友元也只能訪問(wèn)它自己的全體成員,以及從基類繼承到的public和protected成員。**而它的基類和派生類并不認(rèn)可這種友元關(guān)系,按照規(guī)則只能訪問(wèn)公有成員。

繼承和靜態(tài)的關(guān)系

  • 基類定義了static靜態(tài)成員,則整個(gè)繼承體系里面只有一個(gè)這樣的成員。無(wú)論派生出多少個(gè)子類,都只有一個(gè)static成員實(shí)例 。
  • 基類靜態(tài)成員為繼承層次結(jié)構(gòu)所有類共享;

代碼示例:

class Person
{
public :
 Person () {++ _count ;}
protected :
 string _name ; // 姓名
public :
 static int _count; // 統(tǒng)計(jì)人的個(gè)數(shù)。
};
int Person :: _count = 0;
class Student : public Person
{
protected :
 int _stuNum ; // 學(xué)號(hào)
};
class Graduate : public Student
{
protected :
 string _seminarCourse ; // 研究科目
};
void TestPerson()
{
 Student s1 ;
 Student s2 ;
 Student s3 ;
 Graduate s4 ;
 cout <<" 人數(shù) :"<< Person ::_count << endl;
 Student ::_count = 0;
 cout <<" 人數(shù) :"<< Person ::_count << endl;
}

??7、復(fù)雜的菱形繼承及菱形虛擬繼承

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

在這里插入圖片描述

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

在這里插入圖片描述

菱形繼承:菱形繼承是多繼承的一種特殊情況。

在這里插入圖片描述

菱形繼承的問(wèn)題:從下面的對(duì)象成員模型構(gòu)造,可以看出菱形繼承有數(shù)據(jù)冗余和二義性的問(wèn)題。在Assistant的對(duì)象中Person成員會(huì)有兩份。

需要顯示指定訪問(wèn)哪個(gè)父類的成員可以解決二義性問(wèn)題

class Person
{
public:
	string _name; // 姓名
};
class Student : public Person
{
protected:
	int _num; //學(xué)號(hào)
};
class Teacher : public Person
{
protected:
	int _id; // 職工編號(hào)
};
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修課程
};
void Test()
{
	// 這樣會(huì)有二義性無(wú)法明確知道訪問(wèn)的是哪一個(gè)
	Assistant a;
	//a._name = "peter";
	// 需要顯示指定訪問(wèn)哪個(gè)父類的成員可以解決二義性問(wèn)題,但是數(shù)據(jù)冗余問(wèn)題無(wú)法解決
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
}

解決二義性的方法除了指定訪問(wèn)的父類,也可以通過(guò)虛擬繼承.
虛擬基類:使得派生類中只存在同一基類的一份拷貝,解決了基類 數(shù)據(jù)成員的二義性問(wèn)題;
??虛擬繼承的定義??

在這里插入圖片描述

舉例:

class Student: virtual  public Person{……}    //Person為虛基類
class Employee: virtual  public Person{……}    //Person為虛基類
class StuEmployee:public Student,public Employee{……}

代碼示例:

class A { 
public:	
	void vf() {
		cout<<"I come from class A"<<endl; }
};
class B: virtual public A{ };
class C: virtual public A{ };
class D: public B, public C{ };
void main(){
	D d;
	d.vf();	// 正確
}

繼承關(guān)系如圖所示——虛基類!

在這里插入圖片描述

??總結(jié)

本文和大家總結(jié)了C++繼承的幾個(gè)要點(diǎn),從繼承的概念、定義出發(fā),再到基類和派生類之間的關(guān)系,賦值兼容轉(zhuǎn)換,再到繼承中的作用域、成員函數(shù)等,再淺談了C++的復(fù)雜菱形繼承等,希望本文對(duì)大家有所幫助!

以上就是一文帶你吃透C++繼承的詳細(xì)內(nèi)容,更多關(guān)于C++ 繼承的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論