最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++的繼承法則詳解

 更新時間:2024年11月18日 10:29:59   作者:成工小白  
本文詳細(xì)介紹了C++中的繼承機制,包括繼承的概念、定義、使用方法、訪問限定符、賦值兼容轉(zhuǎn)換、作用域、默認(rèn)成員函數(shù)、友元關(guān)系、靜態(tài)成員以及單繼承、多繼承和菱形繼承,感興趣的朋友跟隨小編一起看看吧

一、繼承的概念和定義

1、概念

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

2、使用

#include <iostream>
using namespace std;
class Person
{
};
class Student : public Person
{
};
class Teacher : public Person
{
};
int main()
{
}

作用:是將共有的數(shù)據(jù)和方法提取出來放到父類,自己定義自動獨有的成員,使其減少代碼冗余。

3、定義格式

上述中:

Person:稱為父類或者基類。

Student / Teacher:稱為子類或者派生類。

 語法為:子類聲明 :繼承方式 繼承類名

4、繼承關(guān)系和訪問限定符

繼承方式有三種:

訪問限定符也有三種:

注意兩者的區(qū)別與聯(lián)系。

5、繼承父類時成員訪問方式的變化(繼承方式與訪問方式的聯(lián)系)

類成員 / 繼承方式
public 繼承
protected 繼承
private 繼承
基類的 public 成員
派生類的public成員
派生類的protected 成員
派生類的private 成員
基類的 protected 成員
派生類的protected 成員
派生類的protected 成員
派生類的private 成員
基類的 private
在派生類中不可見
在派生類中不可見
在派生類中不可見

注意:不可見不是不存在,存在但是用不了,即使是自己的成員函數(shù)也不可以訪問,只有調(diào)用父類的成員函數(shù)間接訪問。

6、總結(jié)

(1)、基類private成員在派生類中無論以什么方式繼承都是不可見的。這里的不可見是指基類的私
有成員還是被繼承到了派生類對象中,但是語法上限制派生類對象不管在類里面還是類外面
都不能去訪問它。
(2)、基類private成員在派生類中是不能被訪問,如果基類成員不想在類外直接被訪問,但需要在
派生類中能訪問,就定義為protected。可以看出保護成員限定符是因繼承才出現(xiàn)的。
(3)、實際上面的表格我們進行一下總結(jié)會發(fā)現(xiàn),基類的私有成員在子類都是不可見?;惖钠渌?br />成員在子類的訪問方式 == Min(成員在基類的訪問限定符,繼承方式),public > protected 
> private。
(4)、使用關(guān)鍵字class時默認(rèn)的繼承方式是private,使用struct時默認(rèn)的繼承方式是public,不過
最好顯示的寫出繼承方式。
5. 在實際運用中一般使用都是public繼承,幾乎很少使用protetced/private繼承,也不提倡
使用protetced/private繼承,因為protetced/private繼承下來的成員都只能在派生類的類里
面使用,實際中擴展維護性不強

二、父類和子類對象賦值兼容轉(zhuǎn)換

1、賦值兼容也可以叫“切割”:

(1)、派生類對象 可以賦值給 基類的對象 / 基類的指針 / 基類的引用。這里有個形象的說法叫切片或者切割。寓意把派生類中父類那部分切來賦值過去。

2、賦值兼容中對引用和指針的解讀:

其中,r 對象繼承的是父類與子類共有的這部分?jǐn)?shù)據(jù)的別名,指針也是類似。

3、賦值兼容解釋

平時我們知道,C++類型轉(zhuǎn)換的時候是會產(chǎn)生臨時對象的并且該臨時對象具有常屬性,賦值兼容這里子類和父類也是兩種類型,會不會產(chǎn)生臨時對象吶?

答案 :這過程不會產(chǎn)生臨時對象,這是語法規(guī)定的特殊處理,證明如下:

三、繼承中的作用域

(一)、介紹:

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

(二)、同名成員:隱藏

同名成員指子類中定義的變量名可以和父類中的變量名相同,這是可以的。因為子類和父類是屬于兩個作用域。此時子類成員會隱藏父類成員,這個過程叫 隱藏。

class Person
{
protected:
	string _name = "父類賦值";
	string _sex;
	int _age;
};
class Student : public Person
{
public :
	void fun()
	{
		cout << _name << endl;
	}
protected:
	string _name = "子類賦值";
	string _tem;
};
int main()
{
	Student s;
	s.fun();
	return 0;
}

如上隱藏過后,默認(rèn)訪問的是子類成員,此時向訪問父類成員需要加上域作用限定符。

實際中,不介意設(shè)計同名成員,這無疑是自己給自己設(shè)坑。

筆試題:

解答:

答案選【d】,首先排除a、c,因為c重寫是在后面多態(tài)部分的知識,所以不作講解。

父類和子類是屬于兩個作用域,而重載是需要再同一作用域中才能構(gòu)成重載,所以b排除,最后,如果是成員函數(shù):子類和父類中只要函數(shù)名相同就會構(gòu)成隱藏關(guān)系(與返回值和參數(shù)列表無關(guān))。

如圖,會發(fā)現(xiàn)調(diào)不到?jīng)]有參數(shù)的fun函數(shù),因為該函數(shù)已經(jīng)被隱藏了。

四、子類的默認(rèn)成員函數(shù)

(一)、介紹:

6個默認(rèn)成員函數(shù),“默認(rèn)”的意思就是指我們不寫,編譯器會變我們自動生成一個,那么在派生類
中,這幾個成員函數(shù)是如何生成的呢?
1. 派生類的構(gòu)造函數(shù)必須調(diào)用基類的構(gòu)造函數(shù)初始化基類的那一部分成員。如果基類沒有默認(rèn)
的構(gòu)造函數(shù),則必須在派生類構(gòu)造函數(shù)的初始化列表階段顯示調(diào)用。
2. 派生類的拷貝構(gòu)造函數(shù)必須調(diào)用基類的拷貝構(gòu)造完成基類的拷貝初始化。
3. 派生類的operator=必須要調(diào)用基類的operator=完成基類的復(fù)制。
4. 派生類的析構(gòu)函數(shù)會在被調(diào)用完成后自動調(diào)用基類的析構(gòu)函數(shù)清理基類成員。因為這樣才能
保證派生類對象先清理派生類成員再清理基類成員的順序。
5. 派生類對象初始化先調(diào)用基類構(gòu)造再調(diào)派生類構(gòu)造。
6. 派生類對象析構(gòu)清理先調(diào)用派生類析構(gòu)再調(diào)基類的析構(gòu)。
7. 因為后續(xù)一些場景析構(gòu)函數(shù)需要構(gòu)成重寫,重寫的條件之一是函數(shù)名相同(這個我們后面會講
解)。那么編譯器會對析構(gòu)函數(shù)名進行特殊處理,處理成destrutor(),所以父類析構(gòu)函數(shù)不加
virtual的情況下,子類析構(gòu)函數(shù)和父類析構(gòu)函數(shù)構(gòu)成隱藏關(guān)系

(二)、默認(rèn)構(gòu)造函數(shù)

對于默認(rèn)構(gòu)造函數(shù),可以結(jié)合自定義類型思考:

(1)、子類獨有成員:按照內(nèi)置類型和自定義類型分別處理

(2)、父類的成員:會調(diào)用父類的構(gòu)造函數(shù)。

1、如何顯示寫繼承構(gòu)造函數(shù)

class Person
{
public:
    Person(string name)
    {
	    cout << "調(diào)用父類的構(gòu)造" << endl;
    }
protected:
	string _name ="父類成員";
};
class Student : public Person
{
public :
	void fun()
	{
		cout << Person::_name << endl;
	}
protected:
	string _tem ="子類成員";
};
int main()
{
	return 0;
}

錯誤寫法:

正確寫法:

隱式寫法:

有點像自定義類型的處理方式,需要調(diào)用父類的默認(rèn)構(gòu)造,若父類沒有默認(rèn)構(gòu)造,則會報錯。

顯示寫法:

class Person
{
public:
	Person(string name)
	{
		cout << "調(diào)用父類的構(gòu)造" << endl;
	}
protected:
	string _name = "父類成員";
};
class Student : public Person
{
public :
	Student(string name, string tem) :
		Person(name),
		_tem(tem)
	{
		cout << "調(diào)用子類的構(gòu)造" << endl;
	}
	void fun()
	{
		cout << Person::_name << endl;
	}
protected:
	string _tem = "子類成員";
};
int main()
{
	Student s("1","1");
	return 0;
}

無論怎么寫,都是先調(diào)用父類的構(gòu)造函數(shù),再調(diào)用子類自己的構(gòu)造函數(shù),因為初始化列表的初始化順序是根據(jù)成員聲明的順序確定的,而父類的成員變量在內(nèi)存中始終在前面先聲明。

2、顯示寫拷貝構(gòu)造函數(shù)

顯示寫拷貝構(gòu)造,因為跟自定義類型處理方式類似,子類成員調(diào)用子類的拷貝構(gòu)造,父類成員調(diào)用父類的拷貝構(gòu)造,所以我們需要知道誰是父類的成員,這里就需要用到賦值兼容的知識,也就是切割問題,直接把子類對象傳遞給父類的拷貝構(gòu)造,父類會自己割出自己的那部分。

Student(const Student& s):
	Person(s),
	_tem(s._tem)
{
	cout << "調(diào)用子類的拷貝構(gòu)造";
}

3、賦值重載的寫法

與拷貝構(gòu)造類似的,也是使用切割:

錯誤寫法:

因為子類賦值重載與父類的賦值重載函數(shù)名相同,所以構(gòu)成隱藏關(guān)系,所以這樣寫的話是子類自己無限遞歸了,這時我們要調(diào)用父類的賦值重載就需要指定作用域:

正確寫法:

Student& operator = (const Student& s)
{
	if (this != &s)
	{
		Person::operator=(s);
		_tem = s._tem;
	}
	return *this;
}

4、析構(gòu)函數(shù)的寫法:

錯誤寫法:

原因如下:

C++特殊規(guī)定:子類的析構(gòu)函數(shù)和父類的析構(gòu)函數(shù)會構(gòu)成隱藏關(guān)系,由于后面多態(tài)的遠(yuǎn)呀,析構(gòu)函數(shù)被特殊處理了,父類子類的析構(gòu)函數(shù)的函數(shù)名最后都會被處理成destruuctor(),所以會構(gòu)成隱藏關(guān)系。

正確寫法:

既然是隱藏關(guān)系,所以指定作用域:

但我們會發(fā)現(xiàn)居然調(diào)用了兩次析構(gòu),原因是這里又做了特殊處理:

為了保證先調(diào)用子類析構(gòu),后調(diào)用父類析構(gòu),父類的析構(gòu)會在子類析構(gòu)后自動調(diào)用,所以不需要我們手動調(diào)用:

~Student()
{
	cout << "調(diào)用子類析構(gòu)" << endl;
}

問題:為什么要先先調(diào)用子類析構(gòu),后調(diào)用父類析構(gòu)?

解答:

(1)、為了保持構(gòu)造函數(shù)的調(diào)用順序規(guī)則。

(2)、因為子類的析構(gòu)函數(shù)中可能會使用父類的成員,若先析構(gòu)父類,父類資源就已經(jīng)清理釋放,而子類析構(gòu)中還可能會去訪問父類成員,就可能造成野指針等問題。顯示調(diào)用就無法保證一定會先子后父,所以設(shè)置成自動調(diào)用。

5、繼承類模板

要繼承類模板就必須顯示實例化繼承:

五、繼承與友元

友元關(guān)系不能繼承,也就是說基類友元不能訪問子類私有和保護成員(理解爸爸的朋友不是我的朋友)。

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

基類定義了static靜態(tài)成員,則整個繼承體系里面只有一個這樣的成員。無論派生出多少個子 類,都只有一個static成員實例 。 運用: 在父類的構(gòu)造函數(shù)中 ++靜態(tài)成員,可以統(tǒng)計父類和子類創(chuàng)建的對象個數(shù)。

七、單繼承、多繼承、菱形繼承

1、單繼承:

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

2、多繼承

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

注意:父類用“ , ”隔開。

3、菱形繼承

有了多繼承就可能出現(xiàn)菱形繼承,菱形繼承:菱形繼承是多繼承的一種特殊情況。

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

class Person
{
public:
	Person(string name)
	{
		cout << "調(diào)用父類的構(gòu)造" << endl;
	}
	Person(const Person& p)
	{
		cout << "調(diào)用父類的拷貝構(gòu)造" << endl;
	}
	Person& operator = (const Person& s)
	{
		return *this;
	}
	~Person()
	{
		cout << "調(diào)用父類析構(gòu)" << endl;
	}
protected:
	string _name = "父類成員";
};
class Student : public Person
{
public:
	Student(string name, string tem) :
		Person(name),
		_tem(tem)
	{
		cout << "調(diào)用子類的構(gòu)造" << endl;
	}
	Student(const Student& s) :
		Person(s),
		_tem(s._tem)
	{
		cout << "調(diào)用子類的拷貝構(gòu)造";
	}
	Student& operator = (const Student& s)
	{
		if (this != &s)
		{
			Person::operator=(s);
			_tem = s._tem;
		}
		return *this;
	}
	~Student()
	{
		cout << "調(diào)用子類析構(gòu)" << endl;
	}
	void fun()
	{
		cout << Person::_name << endl;
	}
protected:
	string _tem = "子類成員";
};
class Teacher :public Person
{
};
class Assistant :Teacher, Student
{
};
int main()
{
	Assistant s;
	s._name;
	return 0;
}

對_name訪問不明確,因為既有來自Student的_name,還有來自Teacher的_name。 第一種解決方法是: 訪問_name時可以指明作用域,但是這樣只能暫時解決二義性的問題,沒有解決本質(zhì)問題,有些信息不可能存在兩份,如年齡,學(xué)號等等,這樣設(shè)置就會存在空間浪費。 第二種也是最合適的解決方法:使用虛繼承,如下:

解決菱形繼承的問題:虛繼承virtual

在造成二義性的父類(即最頂層的父類)的直接子類繼承處,加個virtual關(guān)鍵字:

菱形繼承的使用場景:

菱形問題的底層分析:

2:46:00,偏移量等等知識。

到此這篇關(guān)于探索C++的繼承法則的文章就介紹到這了,更多相關(guān)C++繼承法則內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++ Boost Graph算法超詳細(xì)精講

    C++ Boost Graph算法超詳細(xì)精講

    這篇文章主要介紹了C++ Boost Graph算法,我門嘗試使用Boost.Graph庫來運行Goldberg的最大流算法。 Boost.Graph將其稱為push_relabel_max_flow
    2022-10-10
  • C語言程序中遞歸算法的使用實例教程

    C語言程序中遞歸算法的使用實例教程

    這篇文章主要介紹了C語言程序中遞歸算法的使用實例教程,遞歸經(jīng)常被用來進行階乘和比較大小等計算工作,文中舉的都是一些基礎(chǔ)的例子,需要的朋友可以參考下
    2016-04-04
  • C++ 三種繼承方式及好處示例詳解

    C++ 三種繼承方式及好處示例詳解

    這篇文章主要為大家介紹了C++ 三種繼承方式及好處示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • C++缺省參數(shù)的具體使用

    C++缺省參數(shù)的具體使用

    缺省參數(shù)是聲明或定義函數(shù)時為函數(shù)的參數(shù)指定一個默認(rèn)值。本文就詳細(xì)的介紹了一下C++缺省參數(shù)的具體使用,具有一定的參考價值,感興趣的可以了解一下
    2022-01-01
  • C++中對象的賦值與復(fù)制操作詳細(xì)解析

    C++中對象的賦值與復(fù)制操作詳細(xì)解析

    對象之間的賦值也是通過賦值運算符“=”進行的。本來賦值運算符“=”只能用來對單個的變量賦值,現(xiàn)在被擴展為兩個同類對象之間的賦值,這是通過對賦值運算符的重載實現(xiàn)的
    2013-10-10
  • C++中std::count函數(shù)介紹和使用場景

    C++中std::count函數(shù)介紹和使用場景

    std::count函數(shù)是一個非常實用的算法,它可以幫助我們快速統(tǒng)計給定值在指定范圍內(nèi)的出現(xiàn)次數(shù),本文主要介紹了C++中std::count函數(shù)介紹和使用場景,感興趣的可以了解一下
    2024-02-02
  • C++冒泡排序算法實例

    C++冒泡排序算法實例

    這篇文章主要介紹了C++冒泡排序算法實例,本文先是介紹了什么是冒泡排序,然后給出了實現(xiàn)代碼,需要的朋友可以參考下
    2014-10-10
  • C++中l(wèi)ist的用法實例講解

    C++中l(wèi)ist的用法實例講解

    list是順序容器的一種,list是一個雙向鏈表,使用list需要包含頭文件list,這篇文章主要給大家介紹了關(guān)于C++中l(wèi)ist的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-11-11
  • c++創(chuàng)建二維動態(tài)數(shù)組與內(nèi)存釋放問題

    c++創(chuàng)建二維動態(tài)數(shù)組與內(nèi)存釋放問題

    這篇文章主要介紹了c++創(chuàng)建二維動態(tài)數(shù)組與內(nèi)存釋放問題,本文通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-06-06
  • C/C++經(jīng)典實例之模擬計算器示例代碼

    C/C++經(jīng)典實例之模擬計算器示例代碼

    最近在看到的一個需求,本以為比較簡單,但花了不少時間,所以下面這篇文章主要給大家介紹了關(guān)于C/C++經(jīng)典實例之模擬計算器的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-10-10

最新評論

横峰县| 临湘市| 宁德市| 获嘉县| 朝阳县| 敖汉旗| 宣武区| 凉山| 九江县| 临沭县| 桑植县| 峡江县| 绥化市| 碌曲县| 滦南县| 通辽市| 若尔盖县| 沙洋县| 犍为县| 四平市| 边坝县| 乌兰浩特市| 准格尔旗| 陕西省| 东辽县| 涪陵区| 改则县| 乌拉特后旗| 朔州市| 宜黄县| 崇阳县| 白朗县| 禹城市| 渑池县| 成安县| 肇源县| 常州市| 政和县| 乌鲁木齐市| 杨浦区| 乌什县|