c++詳解【繼承】

學過c++的人都知道,c++的三大特性:封裝、繼承、多態

我們今天說的是c++的繼承,那么為什么要引入繼承,它有什么特點呢?

首先,繼承的特點是:使代碼復用,為后面學習多態做鋪墊。

繼承分為:私有繼承(private)、公有繼承(public)、保護繼承(protected)

分別舉例介紹一下它們各自的特性吧:

一、繼承

私有繼承:

//Inherit.h

#pragma once
#include<iostream>
using namespace std;class Base
{
public:Base(){}void FunTest(){cout<<_pri<<endl;}int _pub;
protected:int _pro;
private:int _pri;
};class Derived:private Base         //私有繼承基類Base
{
public:Derived(){}void Function(){cout<<_pub<<endl;}
private:int _d;
};
//Inherit.cpp

#include"Inherit.h"int main()
{Base b;Derived d;d._pri = 10;         //errord._pro = 20;         //errord._pub = 30;         //errord.Function();return 0;
}

通常我們把繼承自基類的類稱為派生類

運行結果:


由上述結果可知,對于私有繼承下的派生類來說,在基類中的公有成員及成員函數繼承到派生類就變為私有,基類的保護也變為派生的私有,基類的私有則不繼承下去。當然私有繼承下的派生類中的成員及成員函數不能在類外被訪問。

保護繼承:

//Inherit.h

#pragma once
#include<iostream>
using namespace std;class Base
{
public:Base(){}void FunTest(){cout<<_pri<<endl;}int _pub;
protected:int _pro;
private:int _pri;
};class Derived:protected Base
{
public:Derived(){}void Function(){cout<<_pub<<endl;}
private:int _d;
};class Third:protected Derived
{
public:void F(){cout<<_pub<<endl;}
private:int _third;
};
//Inherit.cpp

#include"Inherit.h"int main()
{Base b;Derived d;Third t;d._pri = 10;        //errord._pro = 20;        //errord._pub = 30;        //errord.Function();t.F();return 0;
}

運行結果:


由上述結果可知,對于保護繼承下的派生類來說,在基類的公有成員及成員函數繼承到派生類中就變為私有,基類的保護也變為派生的保護,基類的私有則不繼承下去。此時如果變為保護,則在有繼承關系的類與類之間可以互相訪問,而在類外不可訪問。

公有繼承:

//Inherit.h

#pragma once
#include<iostream>
using namespace std;class Base
{
public:Base(){}void FunTest(){cout<<_pri<<endl;}int _pub;
protected:int _pro;
private:int _pri;
};class Derived:public Base
{
public:Derived(){}void Function(){cout<<_pub<<endl;}
private:int _d;
};class Third:protected Derived
{
public:void F(){cout<<_pub<<endl;cout<<_pro<<endl;}
private:int _third;
};
//Inherit.cpp

#include"Inherit.h"int main()
{Base b;Derived d;Third t;d._pri = 10;           //errord._pro = 20;           //errord._pub = 30;d.Function();t.F();return 0;
}

運行結果:


由上述結果可知,對于公有繼承下的派生類來說,在基類的公有成員及成員函數繼承到派生類中仍為公有(即在類外也可訪問),基類的保護也變為派生的保護,基類的私有則不繼承下去。

下面拿一個表格來總結一下吧~


注:若類中省略以何種方式繼承時,默認為私有繼承。

二、調用順序

例1:

//Inherit.h

#pragma once
#include<iostream>
using namespace std;
class Base
{
public:Base(){cout<<"Base()"<<endl;}~Base(){cout<<"~Base()"<<endl;}
private:int _b;
};class Derived:public Base
{
public:Derived(){cout<<"Derived()"<<endl;}~Derived(){cout<<"~Derived()"<<endl;}
private:int _d;
};
//Inherit.cpp

#include"Inherit.h"
int main()
{Base b;Derived d;getchar();return 0;
}
運行結果:



調用順序分析:

Base b;創建一個基類的對象b,則調用基類的構造函數;Derived d;創建一個派生類對象d,則調用派生類的構造函數,此時這里沒有直接的調用派生類的構造函數,而是調用基類的構造函數,最后再調用派生類的構造函數。(在派生類對象調用構造函數時,是在派生類對象構造函數的初始化列表中調用了基類的構造函數)

如派生類構造函數原型為:

	Derived():Base(){cout<<"Derived()"<<endl;}
例2:

//Inherit.h

#pragma once
#include<iostream>
using namespace std;class Base
{
public:Base(){cout<<"Base()"<<endl;}~Base(){cout<<"~Base()"<<endl;}
private:int _b;
};class C
{
public:C(){cout<<"C()"<<endl;}~C(){cout<<"~C()"<<endl;}
};
class Derived:public Base
{
public:Derived():Base(){cout<<"Derived()"<<endl;}~Derived(){cout<<"~Derived()"<<endl;}
private:C _c;
};
//Inherit.cpp

#include"Inherit.h"
int main()
{Base b;Derived d;getchar();return 0;
}
運行結果:



構造函數的調用次序:

初始化列表的構造函數-->成員變量的構造函數-->派生類的構造函數

三、繼承體系的作用域

要知道,繼承體系中基類和派生類是在兩個不同的作用域中。

類中有很多函數,那么當基類的成員函數和派生類的成員函數同名了怎么辦呢?它會調用哪個函數呢?

例:

//Inherit.h

#pragma once
#include<iostream>
using namespace std;
class Base
{
public:Base(){}void Fun(){cout<<"Base::Fun()"<<endl;}
protected:int _b;
};class Derived
{
public:Derived(){}void Fun(){cout<<"Derived::Fun()"<<endl;}
private:int _d;
};
//Inherit.cpp

#include"Inherit.h"
int main()
{Base b;Derived d;d.Fun();system("pause");return 0;
}
運行結果:



當用派生類的對象調用與基類同名的函數的時候,派生類會調用自己的成員函數。把基類的函數隱藏起來。這被稱作同名隱藏。(只要函數名相同,就會出現同名隱藏)

四、賦值兼容規則

關于賦值,大家再熟悉不過了吧。有同一類型的賦值,有不同類型的賦值(可能會用到強制類型轉換哦)。

這里,我們要掌握的是基類與派生類之間的賦值;

例:

1、派生類對象賦給基類對象;(切割、切片)

2、基類對象不能賦給派生類對象;

3、基類的指針或引用指向派生類對象;

4、派生類的指針或引用不能指向基類的對象。(可以通過強制類型轉換完成)

例:

//Inherit.h

#pragma once
#include<iostream>
using namespace std;
class A
{
public:A(){cout<<"A()"<<endl;}int _a;
};class B:public A
{
public:B(){cout<<"B()"<<endl;}int _b;
};

//Inherit.cpp

#include"Inherit.h"
int main()
{A a;B b;a = b;b = a;       //error,無法將基類對象賦給派生類對象A *pA = NULL;B *pB = NULL;pA = pB;pB = pA;     //error,無法讓派生類指針給<span style="font-family: Arial, Helvetica, sans-serif;">基</span><span style="font-family: Arial, Helvetica, sans-serif;">類指針</span>
A &aa = b;B &bb = a;   //error,無法讓派生類的引用給基類對象return 0;
}
上述錯誤的賦值和指向可以通過強制類型轉化。

改:



五、幾種不能繼承的關系

1、友元關系不能繼承。

2、對于靜態成員及靜態成員函數,則整個繼承體系中只有一個這樣的成員。無論派生出多少個子類,都只有一個這樣的static成員實例。

例1:

//Inherit.h

#pragma once
#include<iostream>
using namespace std;
#include<string>
class Person
{friend void Display(Person &p,Student &s);
public:string p_name;
};
class Student:public Person
{
public:string s_no;
};void Display(Person &p,Student &s)
{cout<<p.p_name<<endl;cout<<s.p_name<<endl;cout<<s.s_no<<endl;
}
//Inherit.cpp

#include"Inherit.h"
int main()
{Person p;Student s;Display(p,s);           //error,無法識別Studentreturn 0;
}

這里說明的就是友元關系不能繼承。

例2:

//Inherit.h

#pragma once
#include<iostream>
using namespace std;
#include<string>
class Person
{
public:Person(){cout<<"Person()"<<endl;++_count;}
protected:string _name;
public:static int _count;
};int Person::_count = 0;class Student:public Person
{
protected:int _num;
};class Graduate:public Student
{
protected:string _course;
};
//Inherit.cpp

#include"Inherit.h"
int main()
{Student s;Graduate g;cout<<Person::_count<<endl;cout<<Student::_count<<endl;Student::_count = 0;cout<<Student::_count<<endl;cout<<Person::_count<<endl;return 0;
}
運行結果:


注:友元函數和static成員函數都沒有this指針。

六、單繼承&多繼承

上述講述的都是單繼承的形式,那么多繼承是什么樣子的呢?

例:

class A
{
public:A(){}
protected:int _a;
};class B
{
public:B(){}
protected:int _b;
};class C:public A ,public B             //實現多繼承,c既可以訪問A類的公有和保護成員,也可以訪問B類的公有和保護成員。
{
public:C(){}
protected:int _c;
}

七、菱形繼承(也稱鉆石繼承)

形如:



代碼如下:

Inherit.h

#pragma once
#include<iostream>
using namespace std;
class B
{
public:B(){}
protected:int _b;
};
class C1:public B
{
public:C1(){}
protected:int _c1;
};class C2:public B
{
public:C2(){}
protected:int _c2;
};class D:public C1,public C2
{
public:D(){}
private:int _d;
};

Inherit.cpp

#include"Inherit.h"
int main()
{B b;C1 c1;C2 c2;D d;cout<<sizeof(B)<<endl;           //打印類B中成員,所以它的size為4cout<<sizeof(C1)<<endl;          //打印類C1中的成員,并且繼承了類B,所以它的size為8cout<<sizeof(C2)<<endl;          //打印類C2中的成員,并且繼承了類B,所以它的size為8cout<<sizeof(D)<<endl;           //打印類D的成員,并且繼承了類C1,C2,所以它的size為20return 0;
}
運行結果:



內存中是這樣的形式出現的:



八、虛繼承

為了解決菱形繼承的二義性問題,又引出了虛繼承的概念。在內存中添加了一個地址,用來存放偏移量。雖然浪費了空間,但是解決了二義性問題。

就如上述菱形繼承一樣,通常,我們在用D d;創建一個對象之后,用派生類的對象調用基類的成員時,不知道到底是C1還是C2繼承下來的成員,這時就會出現二義性。為了解決上述問題的二義性,我們引出了虛繼承。

例:

Inherit.h

class B
{
public:B(){}
protected:int _b;
};class C1:virtual public B
{
public:C1(){}
protected:int _c1;
};class C2:virtual public B
{
public:C2(){}
protected:int _c2;
};class D:public C1,public C2
{
public:D(){}
private:int _d;
};

Inherit.cpp

int main()
{B b;C1 c1;C2 c2;D d;cout<<sizeof(B)<<endl;  //只有一個成員,所以size為4cout<<sizeof(C1)<<endl; //類C1自己的成員和繼承自B的成員,還有一個存放偏移量的虛地址,size為12cout<<sizeof(C2)<<endl; //類C2自己的成員和繼承自B的成員,還有一個存放偏移量的虛地址,size為12cout<<sizeof(D)<<endl;  //繼承自C1,C2,所以size為24return 0;
}
運行結果:



形式如圖所示:



繼承的內容就先說到這里啦,講的不詳細的還希望大家能給出建議哦。

歡迎大家來訪~~吐舌頭吐舌頭






本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/382811.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/382811.shtml
英文地址,請注明出處:http://en.pswp.cn/news/382811.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

centOS6.5如何從啟動界面直接進入命令行界面和如何從圖形界面進入命令行界面

centOS6.5如何從啟動界面直接進入命令行界面 編輯 /etc/inittab 將 id:5:initdefault: 修改為 id:3:initdefault: 下次重啟就不啟動X Window了 如何從圖形界面進入命令行界面 startx

優酷解析 轉載的

轉自 https://blog.csdn.net/qq_39797956/article/details/88076404

【送給Git初學者】

好多人都聽過Git吧&#xff0c;目前最流行的分布式版本管理系統。還有好多類似的cvs、svn&#xff08;速度慢、必須聯網&#xff0c;這些是集中式版本控制系統&#xff09;..... 那么&#xff0c;它是用來干什么的呢&#xff1f;舉個例子可能更好理解吧&#xff01; 比如你寫…

虛擬機中的Linux安裝VMware Tools的方法

虛擬機中的Linux安裝VMware Tools的方法 http://www.jb51.net/softjc/189144.html 當.pl文件無法執行時 chmod install-vmware.pl./ install-vmware.pl 安裝就可。 先以root身份登入。 VMware Tools所在位置&#xff1a;VMware 安裝路徑 \VMware\VMware Workstation\linux…

appium 設置參數

appium 配置好環境變量以后&#xff0c; 需要設置啟動參數&#xff0c; 設備名稱&#xff0c; 應用的一些信息主要有以下信息&#xff1a; {"platformName": "Android","platformVersion": "5.1.1","deviceName": "ee…

遠程倉庫

上節我們安裝好了git&#xff0c;并配置好git&#xff0c;github之間的ssh。這節我們就開始用git管理我們的倉庫吧。&#xff08;這節在windows下安裝的git bash上給大家演示吧&#xff09; 首先&#xff0c;創建好一個倉庫&#xff0c;主要步驟如下&#xff1a; 創建好倉庫后…

linux根目錄的意義和內容

1.du命令&#xff1a;du [選項] 文件 ????(1)功能該命令是顯示指定文件以及下的所有文件占用系統數據塊的情況&#xff0c;如果沒有文件&#xff0c;默認為是當前工作目錄 ????-a ???顯示所有文件對系統數據塊的使用情況 ????-b ???顯示數據塊大小時以字節…

c++詳解【智能指針】

智能指針&#xff1f;是一個指針嗎&#xff1f;這里給大家說的是&#xff0c;它不是一個指針&#xff0c;但它模擬了指針所具有的功能。那么&#xff0c;為什么要有智能指針的引入呢&#xff1f;看看下面的例子吧~ void FunTest() {int *p new int[10];FILE *pFile fopen(&qu…

python 使用 os的 popen(‘命令’) 如果命令行輸出中 有中文亂碼, 提示 'gbk' 無法解析的錯誤 解決辦法

os.chdir(‘你的命令’) res os.popen(v.testcomman)print(tempstream.buffer.read().decode(encodingutf-8)&#xff09;

node.js async await 配合Promise對象使用

function getData(){return new Promise(function(resolve, reject){setTimeout(function(){var uname zhang;console.log(this is timeout);resolve(uname);}, 1000);}); } //await 配合 promiese 的 resolve 使用 就會真的等待 同步 async function test(){console.log(1);v…

c++【深度剖析shared_ptr】

shared_ptr解決了scoped_ptr管理單個對象的缺陷&#xff0c;且解決了防拷貝的問題。shared_ptr可以管理多個對象&#xff0c;并且實現了資源共享。 但是仍然存在一些問題&#xff0c;比如&#xff0c;我們熟悉的雙向鏈表&#xff1a; struct Node { Node(const int& value…

centos重新安裝yum

1.備份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 2.下載新的CentOS-Base.repo 到/etc/yum.repos.d/ wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo 3. yum makecache GDB的安裝 yum…

Electron 渲染進程,如何解決require is not defined的問題

mainWindow new BrowserWindow({webPreferences: {nodeIntegration: true}}) // nodeIntegration: true 加上這一句 就可以了 5.0以后默認是false

c++詳解【new和delete】

說起new和delete&#xff0c;了解過c的人應該都知道吧&#xff0c;它是用來分配內存和釋放內存的兩個操作符。與c語言中的malloc和free類似。 c語言中使用malloc/calloc/realloc/free進行動態內存分配&#xff0c;malloc/calloc/realloc用來在堆上分配空間&#xff0c;free將申…

vim 的配置文件 #vim ~/.vimrc

set hlsearch set backspace2 set nu set showmode set ruler set autoindent syntax on set smartindent set tabstop4 set shiftwidth4 set expandtab imap { {}iV

關于tornado的異步耗時操作假設

tornado 如果遇到耗時的操作&#xff0c;可不可以這樣 把耗時操作放在一個由 python進程池維護的 pool中&#xff0c; 用 webapi封裝起來&#xff0c; 然后tornado 接收客戶端請求后&#xff0c;遇到耗時操作就 與訪問另一個webapi &#xff0c; webapi去調用進程池 這種模型不…

Stack/Queue與Vector/List的聯系

Vector:(順序表【數組存儲】) 1.當申請的空間不足的時候&#xff0c;需要再次開辟一塊更大的空間&#xff0c;并把值拷過去。 2.對于尾刪和尾插是比較方便的&#xff0c;只需要改動最后一個元素即可。不會改動原有的空間。適用于多次重復的對尾部插刪。 3.順序存儲&#xff…

利用SetConsoleTextAttribute函數設置控制臺顏色

原文出處&#xff1a; https://blog.csdn.net/odaynot/article/details/7722240 混合顏色 #include <windows.h> #include <iostream> using namespace std;int main() {HANDLE hOut;hOut GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleTextAttribute(hOut,FOREG…

用棧實現后綴表達式求解問題

一、問題概述&#xff1a; 人們經常書寫的數學表達式屬于中綴表達式&#xff0c;今天要解決的是&#xff0c;后綴表達式的求解問題。 如下圖分別為舉例的中綴表達式和后綴表達式&#xff1a; 二、解決思路 我們用棧存儲后綴表達式中的數據部分&#xff0c;當遇到操作符時就取…