激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - C/C++ - 詳解C++中基類與派生類的轉(zhuǎn)換以及虛基類

詳解C++中基類與派生類的轉(zhuǎn)換以及虛基類

2021-03-14 15:10C++教程網(wǎng) C/C++

這篇文章主要介紹了詳解C++中基類與派生類的轉(zhuǎn)換以及虛基類,是C++入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下

C++基類派生類的轉(zhuǎn)換
在公用繼承、私有繼承和保護(hù)繼承中,只有公用繼承能較好地保留基類的特征,它保留了除構(gòu)造函數(shù)和析構(gòu)函數(shù)以外的基類所有成員,基類的公用或保護(hù)成員的訪問權(quán)限在派生類中全部都按原樣保留下來了,在派生類外可以調(diào)用基類的公用成員函數(shù)訪問基類的私有成員。因此,公用派生類具有基類的全部功能,所有基類能夠?qū)崿F(xiàn)的功能, 公用派生類都能實現(xiàn)。而非公用派生類(私有或保護(hù)派生類)不能實現(xiàn)基類的全部功能(例如在派生類外不能調(diào)用基類的公用成員函數(shù)訪問基類的私有成員)。因此,只有公用派生類才是基類真正的子類型,它完整地繼承了基類的功能。

不同類型數(shù)據(jù)之間在一定條件下可以進(jìn)行類型的轉(zhuǎn)換,例如整型數(shù)據(jù)可以賦給雙精度型變量,在賦值之前,把整型數(shù)據(jù)先轉(zhuǎn)換成為雙精度型數(shù)據(jù),但是不能把一個整型數(shù)據(jù)賦給指針變量。這種不同類型數(shù)據(jù)之間的自動轉(zhuǎn)換和賦值,稱為賦值兼容。現(xiàn)在要討論 的問題是:基類與派生類對象之間是否也有賦值兼容的關(guān)系,可否進(jìn)行類型間的轉(zhuǎn)換?

回答是可以的。基類與派生類對象之間有賦值兼容關(guān)系,由于派生類中包含從基類繼承的成員,因此可以將派生類的值賦給基類對象,在用到基類對象的時候可以用其子類對象代替。具體表現(xiàn)在以下幾個方面。

1) 派生類對象可以向基類對象賦值

可以用子類(即公用派生類)對象對其基類對象賦值。如

?
1
2
3
A a1; //定義基類A對象a1
B b1; //定義類A的公用派生類B的對象b1
a1=b1; //用派生類B對象b1對基類對象a1賦值


在賦值時舍棄派生類自己的成員。也就是“大材小用”,如圖
詳解C++中基類與派生類的轉(zhuǎn)換以及虛基類
實際上,所謂賦值只是對數(shù)據(jù)成員賦值,對成員函數(shù)不存在賦值問題。

請注意,賦值后不能企圖通過對象a1去訪問派生類對象b1的成員,因為b1的成員與a1的成員是不同的。假設(shè)age是派生類B中增加的公用數(shù)據(jù)成員,分析下面的用法:
    a1.age=23;  //錯誤,a1中不包含派生類中增加的成員
    b1.age=21;  //正確,b1中包含派生類中增加的成員

應(yīng)當(dāng)注意,子類型關(guān)系是單向的、不可逆的。B是A的子類型,不能說A是B的子類型。只能用子類對象對其基類對象賦值,而不能用基類對象對其子類對象賦值,理由是顯然的,因為基類對象不包含派生類的成員,無法對派生類的成員賦值。同理,同一基類的不同派生類對象之間也不能賦值。

2) 派生類對象可以替代基類對象向基類對象的引用進(jìn)行賦值或初始化

如已定義了基類A對象a1,可以定義a1的引用變量:

?
1
2
3
A a1; //定義基類A對象a1
B b1; //定義公用派生類B對象b1
A& r=a1; //定義基類A對象的引用變量r,并用a1對其初始化


這時,引用變量r是a1的別名,r和a1共享同一段存儲單元。也可以用子類對象初始化引用變量r,將上面最后一行改為

?
1
A& r=b1; //定義基類A對象的引用變量r,并用派生類B對象b1對其初始化


或者保留上面第3行“A& r=a1;”,而對r重新賦值:

?
1
r=b1; //用派生類B對象b1對a1的引用變量r賦值

注意,此時r并不是b1的別名,也不與b1共享同一段存儲單元。它只是b1中基類部分的別名,r與b1中基類部分共享同一段存儲單元,r與b1具有相同的起始地址。

3) 如果函數(shù)的參數(shù)是基類對象或基類對象的引用,相應(yīng)的實參可以用子類對象。

如有一函數(shù):

?
1
2
3
4
fun: void fun(A& r) //形參是類A的對象的引用變量
{
  cout<<r.num<<endl;
} //輸出該引用變量的數(shù)據(jù)成員num

函數(shù)的形參是類A的對象的引用變量,本來實參應(yīng)該為A類的對象。由于子類對象與派生類對象賦值兼容,派生類對象能自動轉(zhuǎn)換類型,在調(diào)用fun函數(shù)時可以用派生類B的對象b1作實參:

?
1
fun(b1);


輸出類B的對象b1的基類數(shù)據(jù)成員num的值。

與前相同,在fun函數(shù)中只能輸出派生類中基類成員的值。

4) 派生類對象的地址可以賦給指向基類對象的指針變量,也就是說,指向基類對象的指針變量也可以指向派生類對象。

[例] 定義一個基類Student(學(xué)生),再定義Student類的公用派生類Graduate(研究生), 用指向基類對象的指針輸出數(shù)據(jù)。本例主要是說明用指向基類對象的指針指向派生類對象,為了減少程序長度,在每個類中只設(shè)很少成員。學(xué)生類只設(shè)num(學(xué)號),name(名字)和score(成績)3個數(shù)據(jù)成員,Graduate類只增加一個數(shù)據(jù)成員pay(工資)。程序如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <string>
using namespace std;
class Student//聲明Student類
{
public:
  Student(int, string,float); //聲明構(gòu)造函數(shù)
  void display( ); //聲明輸出函數(shù)
private:
  int num;
  string name;
  float score;
};
Student::Student(int n, string nam,float s) //定義構(gòu)造函數(shù)
{
  num=n;
  name=nam;
  score=s;
}
void Student::display( ) //定義輸出函數(shù)
{
  cout<<endl<<"num:"<<num<<endl;
  cout<<"name:"<<name<<endl;
  cout<<"score:"<<score<<endl;
}
class Graduate:public Student //聲明公用派生類Graduate
{
public:
 Graduate(int, string ,float,float); //聲明構(gòu)造函數(shù)
 void display( ); //聲明輸出函數(shù)
private:
 float pay; //工資
};
//定義構(gòu)造函數(shù)
Graduate::Graduate(int n, string nam,float s,float p):Student(n,nam,s),pay(p){ }
void Graduate::display() //定義輸出函數(shù)
{
  Student::display(); //調(diào)用Student類的display函數(shù)
  cout<<"pay="<<pay<<endl;
}
int main()
{
  Student stud1(1001,"Li",87.5); //定義Student類對象stud1
  Graduate grad1(2001,"Wang",98.5,563.5); //定義Graduate類對象grad1
  Student *pt=&stud1; //定義指向Student類對象的指針并指向stud1
  pt->display( ); //調(diào)用stud1.display函數(shù)
  pt=&grad1; //指針指向grad1
  pt->display( ); //調(diào)用grad1.display函數(shù)
}

下面對程序的分析很重要,請大家仔細(xì)閱讀和思考。

很多讀者會認(rèn)為,在派生類中有兩個同名的display成員函數(shù),根據(jù)同名覆蓋的規(guī)則,被調(diào)用的應(yīng)當(dāng)是派生類Graduate對象的display函數(shù),在執(zhí)行Graduate::display函數(shù)過程中調(diào)用Student::display函數(shù),輸出num,name,score,然后再輸出pay的值。

事實上這種推論是錯誤的,先看看程序的輸出結(jié)果:

?
1
2
3
4
5
6
7
num:1001
name:Li
score:87.5
 
num:2001
name:wang
score:98.5

 

前3行是學(xué)生stud1的數(shù)據(jù),后3行是研究生grad1的數(shù)據(jù),并沒有輸出pay的值。

問題在于pt是指向Student類對象的指針變量,即使讓它指向了grad1,但實際上pt指向的是grad1中從基類繼承的部分。

通過指向基類對象的指針,只能訪問派生類中的基類成員,而不能訪問派生類增加的成員。所以pt->display()調(diào)用的不是派生類Graduate對象所增加的display函數(shù),而是基類的display函數(shù),所以只輸出研究生grad1的num,name,score3個數(shù)據(jù)。

如果想通過指針輸出研究生grad1的pay,可以另設(shè)一個指向派生類對象的指針變量ptr,使它指向grad1,然后用ptr->display()調(diào)用派生類對象的display函數(shù)。但這不大方便。

通過本例可以看到,用指向基類對象的指針變量指向子類對象是合法的、安全的,不會出現(xiàn)編譯上的錯誤。但在應(yīng)用上卻不能完全滿足人們的希望,人們有時希望通過使用基類指針能夠調(diào)用基類和子類對象的成員。如果能做到這點,程序人員會感到方便。后續(xù)章節(jié)將會解決這個問題。辦法是使用虛函數(shù)和多態(tài)性。

C++虛基類詳解
多繼承時很容易產(chǎn)生命名沖突,即使我們很小心地將所有類中的成員變量和成員函數(shù)都命名為不同的名字,命名沖突依然有可能發(fā)生,比如非常經(jīng)典的菱形繼承層次。如下圖所示:
詳解C++中基類與派生類的轉(zhuǎn)換以及虛基類
類A派生出類B和類C,類D繼承自類B和類C,這個時候類A中的成員變量和成員函數(shù)繼承到類D中變成了兩份,一份來自 A-->B-->D 這一路,另一份來自 A-->C-->D 這一條路。

在一個派生類中保留間接基類的多份同名成員,雖然可以在不同的成員變量中分別存放不同的數(shù)據(jù),但大多數(shù)情況下這是多余的:因為保留多份成員變量不僅占用較多的存儲空間,還容易產(chǎn)生命名沖突,而且很少有這樣的需求。

為了解決這個問題,C++提供了虛基類,使得在派生類中只保留間接基類的一份成員。

聲明虛基類只需要在繼承方式前面加上 virtual 關(guān)鍵字,請看下面的例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
using namespace std;
class A{
protected:
  int a;
public:
  A(int a):a(a){}
};
class B: virtual public A{ //聲明虛基類
protected:
  int b;
public:
  B(int a, int b):A(a),b(b){}
};
class C: virtual public A{ //聲明虛基類
protected:
  int c;
public:
  C(int a, int c):A(a),c(c){}
};
class D: virtual public B, virtual public C{ //聲明虛基類
private:
  int d;
public:
  D(int a, int b, int c, int d):A(a),B(a,b),C(a,c),d(d){}
  void display();
};
void D::display(){
  cout<<"a="<<a<<endl;
  cout<<"b="<<b<<endl;
  cout<<"c="<<c<<endl;
  cout<<"d="<<d<<endl;
}
int main(){
  (new D(1, 2, 3, 4)) -> display();
  return 0;
}

運(yùn)行結(jié)果:

?
1
2
3
4
a=1
b=2
c=3
d=4

本例中我們使用了虛基類,在派生類D中只有一份成員變量 a 的拷貝,所以在 display() 函數(shù)中可以直接訪問 a,而不用加類名和域解析符。

請注意派生類D的構(gòu)造函數(shù),與以往的用法有所不同。以往,在派生類的構(gòu)造函數(shù)中只需負(fù)責(zé)對其直接基類初始化,再由其直接基類負(fù)責(zé)對間接基類初始化?,F(xiàn)在,由于虛基類在派生類中只有一份成員變量,所以對這份成員變量的初始化必須由派生類直接給出。如果不由最后的派生類直接對虛基類初始化,而由虛基類的直接派生類(如類B和類C)對虛基類初始化,就有可能由于在類B和類C的構(gòu)造函數(shù)中對虛基類給出不同的初始化參數(shù)而產(chǎn)生矛盾。所以規(guī)定:在最后的派生類中不僅要負(fù)責(zé)對其直接基類進(jìn)行初始化,還要負(fù)責(zé)對虛基類初始化。

有的讀者會提出:類D的構(gòu)造函數(shù)通過初始化表調(diào)了虛基類的構(gòu)造函數(shù)A,而類B和類C的構(gòu)造函數(shù)也通過初始化表調(diào)用了虛基類的構(gòu)造函數(shù)A,這樣虛基類的構(gòu)造函數(shù)豈非被調(diào)用了3次?大家不必過慮,C++編譯系統(tǒng)只執(zhí)行最后的派生類對虛基類的構(gòu)造函數(shù)的調(diào)用,而忽略虛基類的其他派生類(如類B和類C)對虛基類的構(gòu)造函數(shù)的調(diào)用,這就保證了虛基類的數(shù)據(jù)成員不會被多次初始化。

最后請注意:為了保證虛基類在派生類中只繼承一次,應(yīng)當(dāng)在該基類的所有直接派生類中聲明為虛基類,否則仍然會出現(xiàn)對基類的多次繼承。

可以看到:使用多重繼承時要十分小心,經(jīng)常會出現(xiàn)二義性問題。上面的例子是簡單的,如果派生的層次再多一些,多重繼承更復(fù)雜一些,程序員就很容易陷人迷 魂陣,程序的編寫、調(diào)試和維護(hù)工作都會變得更加困難。因此很多程序員不提倡在程序中使用多重繼承,只有在比較簡單和不易出現(xiàn)二義性的情況或?qū)嵲诒匾獣r才使用多重繼承,能用單一繼承解決的問題就不要使用多重繼承。也正由于這個原因,C++之后的很多面向?qū)ο蟮木幊陶Z言(如Java、Smalltalk、C#、PHP等)并不支持多重繼承。

延伸 · 閱讀

精彩推薦
  • C/C++C語言中炫酷的文件操作實例詳解

    C語言中炫酷的文件操作實例詳解

    內(nèi)存中的數(shù)據(jù)都是暫時的,當(dāng)程序結(jié)束時,它們都將丟失,為了永久性的保存大量的數(shù)據(jù),C語言提供了對文件的操作,這篇文章主要給大家介紹了關(guān)于C語言中文件...

    針眼_6702022-01-24
  • C/C++學(xué)習(xí)C++編程的必備軟件

    學(xué)習(xí)C++編程的必備軟件

    本文給大家分享的是作者在學(xué)習(xí)使用C++進(jìn)行編程的時候所用到的一些常用的軟件,這里推薦給大家...

    謝恩銘10102021-05-08
  • C/C++深入理解goto語句的替代實現(xiàn)方式分析

    深入理解goto語句的替代實現(xiàn)方式分析

    本篇文章是對goto語句的替代實現(xiàn)方式進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下...

    C語言教程網(wǎng)7342020-12-03
  • C/C++C/C++經(jīng)典實例之模擬計算器示例代碼

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

    最近在看到的一個需求,本以為比較簡單,但花了不少時間,所以下面這篇文章主要給大家介紹了關(guān)于C/C++經(jīng)典實例之模擬計算器的相關(guān)資料,文中通過示...

    jia150610152021-06-07
  • C/C++詳解c語言中的 strcpy和strncpy字符串函數(shù)使用

    詳解c語言中的 strcpy和strncpy字符串函數(shù)使用

    strcpy 和strcnpy函數(shù)是字符串復(fù)制函數(shù)。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數(shù)使用,感興趣的朋友跟隨小編要求看看吧...

    spring-go5642021-07-02
  • C/C++c++ 單線程實現(xiàn)同時監(jiān)聽多個端口

    c++ 單線程實現(xiàn)同時監(jiān)聽多個端口

    這篇文章主要介紹了c++ 單線程實現(xiàn)同時監(jiān)聽多個端口的方法,幫助大家更好的理解和學(xué)習(xí)使用c++,感興趣的朋友可以了解下...

    源之緣11542021-10-27
  • C/C++C++之重載 重定義與重寫用法詳解

    C++之重載 重定義與重寫用法詳解

    這篇文章主要介紹了C++之重載 重定義與重寫用法詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下...

    青山的青6062022-01-04
  • C/C++C語言實現(xiàn)電腦關(guān)機(jī)程序

    C語言實現(xiàn)電腦關(guān)機(jī)程序

    這篇文章主要為大家詳細(xì)介紹了C語言實現(xiàn)電腦關(guān)機(jī)程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    xiaocaidayong8482021-08-20
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25
主站蜘蛛池模板: 9丨九色丨国产 | 黄色av片在线观看 | 精品国产一区二区三区在线观看 | 欧美日韩亚洲视频 | www.99re14.com| 青草视频在线观看视频 | 色综合视频网 | 亚洲艳情网站 | 国产午夜精品久久久久久免费视 | 亚洲人成中文字幕在线观看 | av在线播放亚洲 | 色婷婷一区二区三区 | 电影一级毛片 | 麻豆小视频在线观看 | 久久精品一区二区三区四区五区 | xnxx 日本19 | 一区在线视频观看 | 97青青草视频| 91久久久久久久一区二区 | 黑人一级片| 国产91精品欧美 | 久久人人爽人人爽人人片av高请 | 中国美女一级黄色片 | 欧美一及 | 操碰| 精国产品一区二区三区 | 老a影视网站在线观看免费 国产精品久久久久久久久久尿 | 双性精h调教灌尿打屁股的文案 | 99999久久久久久 | 羞羞网站视频 | 一级毛片免费在线 | 毛片网站网址 | 久久久久久久免费看 | 精品一区二区久久久久久按摩 | 国产91精品亚洲精品日韩已满 | 日韩理论电影网 | 国产免费观看一区二区三区 | 国产99免费 | 成人午夜免费网站 | 国产精品视频一区二区三区四 | 久久伊人精品热在75 |