




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
繼承與多態(tài)C語言程序設(shè)計目錄content繼承的實(shí)現(xiàn)方式1子類的構(gòu)造函數(shù)順序2多繼承3多態(tài)的實(shí)現(xiàn)方式4虛函數(shù)表5重載、隱藏和覆蓋的區(qū)別613.1繼承的實(shí)現(xiàn)方式繼承機(jī)制允許類自動從一個或更多的類中繼承其特性、行為和數(shù)據(jù)結(jié)構(gòu),允許根據(jù)需要進(jìn)行更具體的定義來建立新類,即派生類。派生類對于基類的繼承提供了代碼的重用性,而派生類的增加部分提供了對原有代碼擴(kuò)充和改進(jìn)的能力。class<派生類名>:<繼承方式><基類名>{<派生新類定義成員>};單繼承定義的格式為class是關(guān)鍵字,<派生類名>是新定義的一個類的名字,它是從<基類名>中派生的,并且是按指定的<繼承方式>派生的。冒號“:”將派生類與基類的名字分開,用于建立派生類和基類之間的層次關(guān)系。13.1.1公有繼承class<派生類名>:public<基類名>{<派生新類定義成員>};繼承方式為public
基類的公有成員和保護(hù)成員的訪問屬性在派生類中不變,而基類的私有成員不可直接訪問?;恜ublicprotectedprivate子類publicprotected不可見13.1.1公有繼承class
Student
//
聲明基類
{
private:
int
num;
string
name;
char
sex;
public:
void
get_value()
{
cin
>>
num
>>
name
>>
sex;
}
void
display()
{
cout
<<
"num:
"
<<
num
<<
endl;
cout
<<
"name:
"
<<
name
<<
endl;
cout
<<
"sex:
"
<<
sex
<<
endl;
}
};
class
Student1:
public
Student
//派生類{
private:
int
age;
string
addr;
public:
void
get_value_1()
{
cin
>>
age
>>
addr;
}
void
display_1()
{
//cout<<"num:
"<<num<<endl;
//
錯誤
//cout<<"name:
"<<name<<endl;
//
錯誤
//cout<<"sex:
"<<sex<<endl;
//
錯誤
cout
<<
"age:
"
<<
age
<<
endl;
//
正確
cout
<<
"address:
"
<<
addr
<<
endl;
//
正確
}
};13.1.2私有繼承class<派生類名>:private<基類名>{<派生新類定義成員>};繼承方式為private基類中的公有成員和保護(hù)成員都以私有成員的身份出現(xiàn)在派生類中,而基類的私有成員在派生類中不可直接訪問?;恜ublicprotectedprivate子類privateprivate不可見class
Student1:
private
Student
{
private:
int
age;
string
addr;
public:
void
display_1()
{
display();
cout
<<
"age:
"
<<
age
<<
endl;
//正確
cout
<<
"address:
"
<<
addr
<<
endl;
//正確
}
};
int
main()
{
Student1
stud1;
stud1.display_1();
return
0;
}13.1.2私有繼承示例13-2私有繼承
13.1.3保護(hù)繼承class<派生類名>:protected<基類名>{<派生新類定義成員>};繼承方式為protected基類的公有成員和保護(hù)成員都以保護(hù)成員的身份出現(xiàn)在派生類中,而基類的私有成員變量不可直接訪問。基類publicprotectedprivate子類protectedprotected不可見
class
Student
//
聲明基類
{
protected:
//
基類保護(hù)成員
int
num;
string
name;
char
sex;
public:
//
基類公用成員
void
display();
};
class
Student1:
protected
Student
{
private:
int
age;
string
addr;
public:
void
display1();
};
void
Student1::display1()
{
cout
<<
"num:
"
<<
num
<<
endl;
//引用基類的保護(hù)成員
cout
<<
"name:
"
<<
name
<<
endl;
cout
<<
"sex:
"
<<
sex
<<
endl;
cout
<<
"age:
"
<<
age
<<
endl;
cout
<<
"address:
"
<<
addr
<<
endl;
}
13.1.3保護(hù)繼承示例13-3保護(hù)繼承
13.1.4訪問控制相關(guān)分析分析圖13.1中不同模塊對x、y、z的訪問特性在A類中可以對x、y、z直接進(jìn)行訪問在B類中可以對x、y直接進(jìn)行訪問,不能對z直接進(jìn)行訪問在C類中可以對x、y直接進(jìn)行訪問,不能對z直接進(jìn)行訪問
平行模塊中可以對x直接進(jìn)行訪問,不能對y、z進(jìn)行訪問
對不允許客戶直接操作的成員變量應(yīng)設(shè)置為私有,并提供接口即公有成員函數(shù)來訪問該變量。在子類中除了繼承的成員,自己添加的新成員也應(yīng)該遵循這種設(shè)計思想。13.1.4訪問控制相關(guān)分析13.2子類的構(gòu)造函數(shù)的順序#include
<iostream.h>
class
animal
{
public:
animal(int
height,
int
weight)
{
cout
<<
"animal
construct"
<<
endl;
}
~animal()
{
cout
<<
"animal
destruct"
<<
endl;
}
void
eat()
{
cout
<<
"animal
eat"
<<
endl;
}
void
sleep()
{
cout
<<
"animal
sleep"
<<
endl;
}
void
breathe()
{
cout
<<
"animal
breathe"
<<
endl;
}
};
class
fish:
public
animal
{
public:
fish()
{
cout
<<
"fish
construct"
<<
endl;
}
~fish()
{
cout
<<
"fish
destruct"
<<
endl;
}
};
void
main()
{
fish
fh;
}示例13-4子類構(gòu)造函數(shù)順序示例
當(dāng)構(gòu)造fish子類的對象fh時,它需要先構(gòu)造animal父類的對象,調(diào)用animal父類的默認(rèn)構(gòu)造函數(shù)(即不帶參數(shù)的構(gòu)造函數(shù))13.2子類的構(gòu)造函數(shù)的順序
子類構(gòu)造函數(shù)中想要調(diào)用父類有參數(shù)的構(gòu)造函數(shù)(或子類要向父類的構(gòu)造函數(shù)傳遞參數(shù))時,可以在成員初始化列表中指明。
格式為:
子類名::子類名(參數(shù)):父類名(參數(shù))13.2子類的構(gòu)造函數(shù)的順序#include<iostream.h>#include<string.h>classPerson{protected: char*pname;public: Person(char*p){ pname=newchar[strlen(p)+1]; strcpy(pname,p);}};
classStudent:publicPerson{protected: intscore; constintID; int&QQ;public:Student(char*p,inti);};Student::Student(char*p,inti):Person(p),ID(1),QQ(t){score=i;}voidmain(){ Students("tom",98);}示例13-5子類調(diào)用父類構(gòu)造函數(shù)13.2子類的構(gòu)造函數(shù)的順序#include
<iostream.h>
class
animal
{
public:
animal(int
height,
int
weight)
{
cout
<<
"animal
construct"
<<
endl;
}
};
class
fish:
public
animal
{
public:
fish()
{
this->animal::animal(400,
300);
}
};
void
main()
{
fish
fh;
}此外還可以使用this指針來調(diào)用父類有參數(shù)的構(gòu)造函數(shù)13.2子類的構(gòu)造函數(shù)的順序class
Person
{
char
*name;
int
age;
char
sex;
public:
Person()
{
name
=
NULL;
}
Person(const
char
*n,
int
a,
char
s):
age(a),
sex(s)
{
name
=
new
char[strlen(n)
+
1];
strcpy(name,
n);
cout
<<
"Person
construct"
<<
endl;
}
~Person()
{
delete
name;
}
};
class
Student:
public
Person
{
Person
contacts;
int
score;
public:
Student(const
char
*n,
int
a,
char
s,
Person
&p,
int
i):contacts(n,
a,
s),
score(i)
{
cout
<<
"Student
construct"
<<
endl;
}
};
int
main()
{
Person
dad("Tom",
40,
'M');
Student
son("Jerry",
15,
'M',
dad,
100);
}當(dāng)子類的新增成員中有父類的對象成員時,也是遵循先執(zhí)行父類構(gòu)造函數(shù),再執(zhí)行子類構(gòu)造函數(shù)13.3多繼承
多繼承是單繼承的擴(kuò)展,所謂的多繼承指的是一個派生類可以有多個基類,派生類與每個基類之間的關(guān)系仍然可以看成是單繼承。多繼承下派生類的定義格式如下:class<派生類名>:<繼承方式1><基類名1>,<繼承方式2><基類名2>……{<派生類類體>};classA{…};classB{…};classC:publicA,publicB{…};例如13.3.2多繼承的構(gòu)造函數(shù)<派生類名>(<總參數(shù)表>):<基類名1>(<參數(shù)表1>),<基類名2>(<參數(shù)表2>),…
<子對象名>(<參數(shù)表n+1>),…{<派生類構(gòu)造函數(shù)體>}
在多繼承的情況下,派生類的構(gòu)造函數(shù)格式如下:
其中,<總參數(shù)表>中各個參數(shù)包含了其后的各個分參數(shù)表。
多繼承下派生類的構(gòu)造函數(shù)與單繼承下派生類構(gòu)造函數(shù)相似,它必須同時負(fù)責(zé)該派生類所有基類構(gòu)造函數(shù)的調(diào)用。
派生類構(gòu)造函數(shù)執(zhí)行順序是先執(zhí)行所屬基類的構(gòu)造函數(shù),再執(zhí)行派生類本身構(gòu)造函數(shù),處于同一層次的各基類構(gòu)造函數(shù)的執(zhí)行順序取決于定義派生類時所指定的各基類順序,與派生類構(gòu)造函數(shù)中所定義的成員初始化列表的各項順序無關(guān)。13.3.2#include
<iostream.h>
class
B1
{
public:
B1(int
i)
{…}};
class
B2
{
public:
B2(int
i)
{…}};
class
B3
{
public:
B3(int
i)
{…}};
class
A:
public
B2,
public
B1
{
public:
A(int
i,
int
j,
int
k,
int
l):
B1(i),
B2(j),
bb(k)
{
a
=
1;
cout
<<
"構(gòu)造函數(shù)
A."
<<
a
<<
endl;
}
private:
int
a;
B3bb;};
例如多繼承的構(gòu)造函數(shù)13.3.3多繼承的二義性問題
一般說來,在派生類中對基類成員的訪問應(yīng)該是唯一的,但是,由于多繼承情況下,可能造成對基類中某成員的訪問出現(xiàn)了不唯一的情況,則稱為對基類成員訪問的二義性問題。解決辦法是通過作用域運(yùn)算符::進(jìn)行限定。class
A
{
public:
void
f();
};
class
B
{
public:
void
f();
void
g();
};
class
C:
public
A,
public
B
{
public:
void
g();
void
h();
};
例如1)對函數(shù)f()的訪問c1.f();便具有二義性,解決辦法c1.A::f()或者c1.B::f()。2)c1.g()不存在二義性,它是指C::g(),而不是指B::g()。因為規(guī)定派生類的成員將支配基類中的同名成員。class
A
{
public:
int
a;
};
class
B1:
public
A
{
private:
int
b1;
};
class
B2:
public
A
{
private:
int
b2;
};
class
C:
public
B1,
public
B2
{
public:
int
f();
private:
int
c;
};當(dāng)一個派生類從多個基類派生而來,而這些基類又有一個共同的基類,則對該基類中聲明的成員進(jìn)行訪問時,也可能會出現(xiàn)二義性。已知:Cc1;下面的兩個訪問都有二義性:
c1.a;
c1.A::a;而下面的兩個訪問是正確的:
c1.B1::a;
c1.B2::a;由于二義性的原因,一個類不可以從同一個類中直接繼承一次以上,例如:
classA:publicB,publicB這是錯誤的。13.3.3多繼承的二義性問題【學(xué)習(xí)提示】(1)派生類對基類成員可以有不同的訪問方式:a)派生類可以覆蓋基類成員b)派生類不能訪問基類私有成員c)公有繼承基類的公有段和保護(hù)段成員訪問權(quán)對派生類保持不變d)私有繼承基類的公有段和保護(hù)段成員成為派生類的私有成員(2)派生類構(gòu)造函數(shù)聲明為派生類構(gòu)造函數(shù)(參數(shù)表):基類(參數(shù)表),對象成員(參數(shù)表)執(zhí)行順序:
先長輩
基類
再客人
對象成員
后自己
派生類13.3.3多繼承的二義性問題13.4多態(tài)的實(shí)現(xiàn)方式 C++多態(tài)性是通過虛函數(shù)來實(shí)現(xiàn)的,虛函數(shù)允許子類重新定義成員函數(shù),而子類重新定義父類的做法稱為覆蓋(override)。而重載(overload)并沒有體現(xiàn)多態(tài)性。
多態(tài)實(shí)現(xiàn)的途徑是聲明基類的指針,利用該指針指向任意一個子類對象,調(diào)用相應(yīng)的虛函數(shù)。多態(tài)與非多態(tài)的實(shí)質(zhì)區(qū)別就是函數(shù)地址是早綁定還是晚綁定。封裝可以使得代碼模塊化,繼承可以擴(kuò)展已存在的代碼,他們的目的都是為了代碼重用。而多態(tài)的目的則是為了接口重用。13.4.1虛函數(shù)的聲明
虛函數(shù)是基類的公有部分或保護(hù)部分的某成員函數(shù),在函數(shù)頭前加上關(guān)鍵字“virtual”,其格式為:classA{public:(或protected:)virtual<返回類型>成員函數(shù)名(參數(shù)表);};13.4.2虛函數(shù)在派生類中的重新定義class
A
{
public:
virtual
void
print()
{
cout
<<
"This
is
A"
<<
endl;
}
};
class
B:
public
A
{
public:
void
print()
{
cout
<<
"This
is
B"
<<
endl;
}
};
虛函數(shù)在派生類重新定義時,可以不再添加關(guān)鍵字virtual,定義格式與普通成員函數(shù)一樣int
main()
{
A
a;
B
b;
A
*p1
=
&a;
A
*p2
=
&b;
p1->print();
p2->print();
return
0;
}
輸出結(jié)果ThisisAThisisB如果把基類中virtual關(guān)鍵字去掉,輸出結(jié)果便成了ThisisAThisisA【幾點(diǎn)說明】13.4.2虛函數(shù)在派生類中的重新定義(1)既然虛函數(shù)使用的目的是為了在多態(tài)的實(shí)現(xiàn)過程中,派生類可以重新實(shí)現(xiàn)基類函數(shù)的定義,那么虛函數(shù)可以在基類中沒有定義,要求任何派生類都定義自己的版本。這種虛函數(shù)稱為純虛函數(shù),其說明形式為: virtual類型
函數(shù)名(參數(shù)表)=0;(2)如果一個類中至少有一個純虛函數(shù),則該類稱為抽象類。抽象類只能用作其他類的基類,抽象類不能建立對象,也不能用作參數(shù)類型、函數(shù)返回類型或顯式轉(zhuǎn)換的類型。但可以聲明抽象類的指針和引用。(3)由于純虛函數(shù)是沒有定義函數(shù)語句的基類虛函數(shù),派生類必須為每一個基類純虛函數(shù)提供相應(yīng)的函數(shù)定義。因此,如果從基類繼承來的純虛函數(shù),在派生類中沒有定義,該虛函數(shù)仍為純虛函數(shù),派生類仍為抽象類。13.4.3基類的析構(gòu)函數(shù)是virtual的
虛析構(gòu)函數(shù)是為了解決基類的指針指向派生類對象,并用基類的指針刪除派生類對象。
如果某個類不包含虛函數(shù),那一般是表示它將不作為一個基類來使用。當(dāng)一個類不準(zhǔn)備作為基類使用時,使析構(gòu)函數(shù)為虛一般是個壞主意。因為它會為類增加一個虛函數(shù)表,使得對象的體積翻倍,還有可能降低其可移植性。
所以基本的一條是:無故的聲明虛析構(gòu)函數(shù)和永遠(yuǎn)不去聲明一樣是錯誤的。實(shí)際上,很多人這樣總結(jié):當(dāng)且僅當(dāng)類里包含至少一個虛函數(shù)的時候才去聲明虛析構(gòu)函數(shù)。
抽象類是準(zhǔn)備被用做基類的,基類必須要有一個虛析構(gòu)函數(shù),純虛函數(shù)會產(chǎn)生抽象類,所以方法很簡單:在想要成為抽象類的類里聲明一個純虛析構(gòu)函數(shù)。class
ClxBase
{
public:
ClxBase(){};
virtual
~ClxBase(){};
virtual
void
DoSomething()
{
cout
<<
"Do
something
in
class
ClxBase!"
<<
endl;
}
};
class
ClxDerived:
public
ClxBase
{
public:
ClxDerived(){};
~ClxDerived()
{
cout
<<
"Output
from
the
destructor
of
class
ClxDerived!"
<<
endl;
}
void
DoSomething()
{
cout
<<
"Do
something
in
class
ClxDerived!"
<<
endl;
}
};
void
main()
{ClxBase*pTest=newClxDerived;pTest->DoSomething();deletepTest;}13.4.3基類的析構(gòu)函數(shù)是virtual的代碼的輸出結(jié)果是:DosomethinginclassClxDerived!OutputfromthedestructorofclassClxDerived!但是,如果把類ClxBase析構(gòu)函數(shù)前的virtual去掉,那輸出結(jié)果就是下面的樣子了:DosomethinginclassClxDerived!析構(gòu)函數(shù)不被調(diào)用的話就會造成內(nèi)存泄漏!虛析構(gòu)函數(shù)示例13.5虛函數(shù)表
虛函數(shù)(VirtualFunction)是通過一張?zhí)摵瘮?shù)表(VirtualTable)來實(shí)現(xiàn)的。簡稱為V-Table。表中是一個類的虛函數(shù)的地址表,用父類的指針來操作一個子類的時候,指明了實(shí)際所應(yīng)該調(diào)用的函數(shù)。 C++的編譯器保證虛函數(shù)表的指針存在于對象實(shí)例中最前面的位置(這是為了保證取到的虛函數(shù)表有最高的性能——如果有多層繼承或是多重繼承的情況下)。這意味著通過對象實(shí)例的地址得到虛函數(shù)表,然后就可以遍歷其中的函數(shù)指針,并調(diào)用相應(yīng)的函數(shù)。classBase{public:virtualvoidf(){cout<<"Base::f"<<endl;}virtualvoidg(){cout<<"Base::g"<<endl;}virtualvoidh(){cout<<"Base::h"<<endl;}};typedefvoid(*Fun)(void);Baseb;FunpFun=NULL;cout<<"虛函數(shù)表地址:"<<(int*)(&b)<<endl;cout<<"虛函數(shù)表—第一個函數(shù)地址:"<<(int*)*(int*)(&b)<<endl;//InvokethefirstvirtualfunctionpFun=(Fun)*((int*)*(int*)(&b));pFun();假設(shè)有這樣的一個類可以通過Base的實(shí)例來得到虛函數(shù)表實(shí)際運(yùn)行經(jīng)果如下:虛函數(shù)表地址:0012FED4虛函數(shù)表—第一個函數(shù)地址:0044F148Base::f13.5虛函數(shù)表13.5虛函數(shù)表(Fun)*((int*)*(int*)(&b)+0);
//Base::f()(Fun)*((int*)*(int*)(&b)+1);
//Base::g()(Fun)*((int*)*(int*)(&b)+2);
//Base::h()通過強(qiáng)行把&b轉(zhuǎn)成int*,取得虛函數(shù)表的地址,然后,再次取址就可以得到第一個虛函數(shù)的地址了,也就是Base::f(),這在上面的程序中得到了驗證(把int*強(qiáng)制轉(zhuǎn)成了函數(shù)指針)。注意:虛函數(shù)表最后有一個結(jié)點(diǎn),這是虛函數(shù)表的結(jié)束結(jié)點(diǎn),就像字符串的結(jié)束符“/0”一樣,其標(biāo)志了虛函數(shù)表的結(jié)束。這個結(jié)束標(biāo)志的值在不同的編譯器下是不同的。13.5虛函數(shù)表一般繼承:無虛函數(shù)覆蓋1)虛函數(shù)按照其聲明順序放于表中。2)父類的虛函數(shù)在子類的虛函數(shù)前面。請注意,在這個繼承關(guān)系中,子類沒有覆蓋任何父類的函數(shù)。對于實(shí)例Derived的虛函數(shù)表:13.5虛函數(shù)表一般繼承:有虛函數(shù)覆蓋對于實(shí)例Derived的虛函數(shù)表:覆蓋父類的虛函數(shù)是很顯然的事情,不然,虛函數(shù)就變得毫無意義。在這個類的設(shè)計中,只覆蓋了父類的一個函數(shù):f()。1)覆蓋的f()函數(shù)被放到了虛表中原來父類虛函數(shù)的位置。2)沒有被覆蓋的函數(shù)依舊。這就實(shí)現(xiàn)了多態(tài)。13.5虛函數(shù)表例#include
<iostream>
using
namespace
std;
class
Base
{
public:
virtual
void
f()
{
cout
<<
"Base::f"
<<
endl;
}
virtual
void
g()
{
cout
<<
"Base::g"
<<
endl;
}
virtual
void
h()
{
cout
<<
"Base::h"
<<
endl;
}
};
class
Derive1:
public
Base
{
virtual
void
f()
{
cout
<<
"Derive1::f"
<<
endl;
}
};
class
Derive2:
public
Base
{
virtual
void
f()
{
cout
<<
"Deriv
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 農(nóng)機(jī)拖車出售合同范例
- 農(nóng)村建房拆遷合同范例
- 買賣門面付款合同范例
- 俄羅斯短期租房合同范例
- 保山糧油購銷合同范例
- 兼職美工合同范例
- 做槽鋼合同范例
- led路燈設(shè)計合同范例
- 光氧環(huán)保合同范例
- 光伏 安裝合同范例
- 污水處理廠安全教育培訓(xùn)
- 護(hù)理工作血液透析操作規(guī)范
- Unit+6+The+Admirable+Lesson+2+History+makers 高一英語北師大版(2019)必修第二冊
- 設(shè)計變更流程圖
- 學(xué)校消防防火月檢查記錄表
- 外墻水包砂施工方案模板
- 無犯罪記錄證明申請表
- 聚酯生產(chǎn)技術(shù) 聚酯工藝技術(shù)
- 2023年四川省綿陽市中考語文試卷真題(含答案)
- 新大象版三年級下冊科學(xué)第二單元《電與我們的生活》全部課件(共5課時)
- 混床計算書(新)
評論
0/150
提交評論