多態(tài)的基本概念
實現(xiàn)了多態(tài)機制的程序铸抑,可以使用同一個名字完成不同的功能。
多態(tài)分為編譯時多態(tài)和運行時多態(tài)。
多態(tài)
靜態(tài)多態(tài)在編譯期間就可以確定函數(shù)的調(diào)用地址偿衰,并產(chǎn)生代碼。
函數(shù)調(diào)用與代碼入口地址的綁定需要在運行時刻才能確定改览,稱為動態(tài)聯(lián)編或動態(tài)綁定下翎。
在類之間滿足賦值兼容的前提下,實現(xiàn)動態(tài)綁定必須滿足以下兩個條件
- 必須聲明虛函數(shù)
- 通過基類類型的引用或者指針調(diào)用虛函數(shù)恃疯。
虛函數(shù)
所謂虛函數(shù)漏设,就是在函數(shù)聲明時前面加了virtual關鍵字的成員函數(shù)。
聲明虛函數(shù)成員的格式
virtual 函數(shù)返回值類型 函數(shù)名(形參表);
通過基類指針實現(xiàn)多態(tài)
程序6-1 通過基類指針實現(xiàn)多態(tài)示例
#include <iostream>
using namespace std;
class A
{
public:
virtual void Print()
{
cout<<"A::Print"<<endl;
}
};
class B:public A
{
public:
virtual void Print()
{
cout<<"B::Print"<<endl;
}
};
class D:public A
{
public:
virtual void Print()
{
cout<<"D::Print"<<endl;
}
};
class E:public B
{
public:
virtual void Print()
{
cout<<"E::Print"<<endl;
}
};
int main()
{
A a; B b; D d; E e;
A *pa = &a; //基類指針pa指向基類對象a
B *pb = &b; //派生類指針pb指向派生類對象B
pa->Print(); //多態(tài)今妄,目前指向基類對象郑口,調(diào)用a.Print(),輸出A::Print
pa = pb; //派生類指針賦給基類指針,pa指向派生類對象b
pa->Print(); //多態(tài)盾鳞,目前指向派生類對象犬性,調(diào)用b.Print(),輸出B::Print
pa = &d; //基類指針pa指向派生類對象d
pa->Print(); //多態(tài),目前指向派生類對象腾仅,調(diào)用d.Print(),輸出D::Print
pa = &e; //基類指針pa指向派生類對象e
pa->Print(); //多態(tài)乒裆,目前指向派生類對象,調(diào)用e.Print(),輸出E::Print
return 0;
}
A::Print
B::Print
D::Print
E::Print
程序6-2 用基類指針訪問基類對象及派生類對象
#include <iostream>
#include <string>
using namespace std;
class A
{
public:
void put_name(string s)
{
name = s;
}
virtual void print_name() const
{
cout<<"A::"<<name<<"\n";
}
string name;
};
class B:public A
{
public:
void put_name(string s)
{
put_name(s);
}
virtual void print_name() const
{
cout<<"B::"<<name<<","<<A::name<<"\n";
}
void put_phone(string num)
{
phone_num = num;
}
void print_phone() const
{
cout<<phone_num<<"\n";
}
string phone_num; //派生類中的成員變量
};
int main()
{
A *A_p;
A A_obj;
B B_obj;
A_p = &A_obj; //基類指針指向基類對象
A_p->put_name("多態(tài)示例_名字"); //賦給基類對象A_obj
cout<<"A_p->print_name的輸出內(nèi)容:\t";
A_p->print_name(); //使用指針輸出推励,A_obj中的值
cout<<"A_obj.print_name()的輸出內(nèi)容:\t";
A_obj.print_name(); //使用對象輸出
A_p = &B_obj; //基類指針指向派生類對象
A_p->put_name("另一個名字"); //多態(tài)鹤耍,賦給派生類對象B_obj
cout<<"A_p->print_name()的輸出內(nèi)容:\t";
A_p->print_name(); //多態(tài),使用指針輸出验辞,B_obj中的值稿黄,繼承的name的值
cout<<"B_obj.print_name()的輸出內(nèi)容:\t";
B_obj.print_name(); //使用對象輸出,B_obj中的值跌造,繼承的name值
B_obj.put_phone("電話號碼999"); //派生類對象賦值
cout<<"((B *)A_p)->print_phone()的輸出內(nèi)容:\t";
((B*)A_p)->print_phone(); //強制轉(zhuǎn)換基類指針杆怕,輸出的是派生類對象中的值
cout<<"B_obj.print_phone()的輸出內(nèi)容:\t\t";
B_obj.print_phone(); //輸出的是派生類對象中的值
return 0;
}
A_p->print_name的輸出內(nèi)容: A::多態(tài)示例_名字
A_obj.print_name()的輸出內(nèi)容: A::多態(tài)示例_名字
A_p->print_name()的輸出內(nèi)容: B::另一個名字,另一個名字
B_obj.print_name()的輸出內(nèi)容: B::另一個名字,另一個名字
((B *)A_p)->print_phone()的輸出內(nèi)容: 電話號碼999
B_obj.print_phone()的輸出內(nèi)容: 電話號碼999
通過基類引用實現(xiàn)多態(tài)
通過基類指針調(diào)用虛函數(shù)時可以實現(xiàn)多態(tài),通過基類的引用調(diào)用虛函數(shù)的語句也是多態(tài)的壳贪。
程序6-3 基類引用實現(xiàn)多態(tài)
#include <iostream>
using namespace std;
class A
{
public:
virtual void Print()
{
cout<<"A::Print"<<endl;
}
};
class B:public A
{
public:
virtual void Print()
{
cout<<"B::Print"<<endl;
}
};
void PrintInfo(A &r)
{
r.Print(); //多態(tài)陵珍,使用基類引用調(diào)用哪個Print()取決于r引用了哪個類的對象
}
int main()
{
A a;
B b;
PrintInfo(a);
PrintInfo(b);
return 0;
}
A::Print
B::Print
*多態(tài)的實現(xiàn)原理
多態(tài)的關鍵在于通過基類指針或引用調(diào)用一個虛函數(shù)時,編譯階段不能確定到底調(diào)用的時基類還是派生類的函數(shù)违施,運行時才能確定互纯。
程序6-4 多態(tài)機制下對象存儲空間的大小
#include <iostream>
using namespace std;
class A
{
public:
int i;
virtual void func(){}
virtual void func2(){}
};
class B:public A
{
int j;
void func(){}
};
int main()
{
cout<<sizeof(A)<<","<<sizeof(B);
return 0;
}
16,16
多態(tài)實例
在面向?qū)ο蟮某绦蛟O計中,使用多態(tài)能夠增強程序的可擴充性磕蒲。
使用多態(tài)也能起到精簡代碼的作用伟姐。
程序6-5 使用多態(tài)出來圖形示例
#include <iostream>
#include <cmath>
using namespace std;
class CShape //基類:圖形類
{
protected:
double acreage; //圖形的面積收苏,子類可以訪問
public:
CShape()
{
// cout<<"基類構造函數(shù)"<<endl;
};
virtual ~CShape()
{
// cout<<"基類析構函數(shù)"<<endl;
};
virtual double CalAcr() //計算面積,虛函數(shù)
{
return 0;
};
virtual void setAcreage(double acre){}; //設置面積值愤兵,虛函數(shù)
virtual void PrintInfo(){}; //顯示信息鹿霸,虛函數(shù)
};
class CRectangle:public CShape //派生類:矩形類
{
double width,high; //矩形的寬度和高度
public:
CRectangle(double w,double h)
{
// cout<<"矩形帶參構造函數(shù)"<<endl;
width = w;
high = h;
};
CRectangle()
{
// cout<<"矩形無參構造函數(shù)"<<endl;
width = 0;
high = 0;
};
~CRectangle()
{
// cout<<"矩形析構函數(shù)"<<endl;
};
virtual double CalAcr(); //繼承的函數(shù),虛函數(shù)
virtual void PrintInfo(); //繼承的函數(shù)秆乳,虛函數(shù)
virtual void setAcreage(double); //繼承的函數(shù)懦鼠,虛函數(shù)
};
class CCircle:public CShape //派生類:圓形類
{
double radius; //半徑,私有的
public:
CCircle(double r)
{
// cout<<"圓形帶參構造函數(shù)"<<endl;
radius = r;
};
CCircle()
{
// cout<<"圓形無參構造函數(shù)"<<endl;
radius = 0;
};
~CCircle()
{
// cout<<"圓形析構函數(shù)"<<endl;
};
virtual double CalAcr(); //繼承的函數(shù)屹堰,虛函數(shù)
virtual void PrintInfo(); //繼承的函數(shù)肛冶,虛函數(shù)
virtual void setAcreage(double); //繼承的函數(shù),虛函數(shù)
};
class CTriangle:public CShape //派生類:三角形類
{
double a,b,c; //三條邊的長度扯键,私有的
public:
CTriangle(double a,double b,double c)
{
// cout<<"三角形帶參構造函數(shù)"<<endl;
this->a = a;
this->b = b;
this->c = c;
};
CTriangle()
{
// cout<<"三角形無參構造函數(shù)"<<endl;
a = 0;
b = 0;
c = 0;
};
~CTriangle()
{
// cout<<"三角形析構函數(shù)"<<endl;
};
virtual double CalAcr(); //繼承的函數(shù)睦袖,虛函數(shù)
virtual void PrintInfo(); //繼承的函數(shù),虛函數(shù)
virtual void setAcreage(double); //繼承的函數(shù)荣刑,虛函數(shù)
};
double CRectangle::CalAcr() //計算矩形面積
{
return width * high;
}
void CRectangle::PrintInfo() //輸出矩形信息
{
cout<<"矩形馅笙。\t寬度="<<this->width<<",高度="<<this->high<<",面積="<<this->acreage<<endl;
}
void CRectangle::setAcreage(double acre) //設置面積值
{
acreage = acre; //訪問基類保護成員
}
double CCircle::CalAcr() //計算圓形的面積
{
return 3.14*radius*radius;
}
void CCircle::PrintInfo() //輸出圓形信息
{
cout<<"圓。\t半徑="<<this->radius<<",面積="<<this->acreage<<endl;
}
void CCircle::setAcreage(double acre)
{
acreage = acre;
}
double CTriangle::CalAcr() //根據(jù)海倫公式計算三角形面積
{
double p = (a+b+c)/2.0;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
void CTriangle::PrintInfo()
{
cout<<"三角形厉亏。三條邊分別是:"<<this->a<<","<<this->b<<","<<this->c<<",面積="<<this->acreage<<endl;
}
void CTriangle::setAcreage(double acre)
{
acreage = acre;
}
CShape *pShapes[100]; //用來存儲各種幾何形狀董习,最對100個
int main()
{
int i,n;
double temp1,temp2,temp3;
CRectangle *pr;
CCircle *pc;
CTriangle *pt;
cin>>n;
for(i=0;i<n;++i)
{
char c;
cin>>c;
switch(c)
{
case 'R':case 'r': //矩形
cin>>temp1>>temp2;
pr = new CRectangle(temp1,temp2);
pr->setAcreage(pr->CalAcr());
pShapes[i] = pr;
break;
case 'C':case 'c':
cin>>temp1; //圓形
pc = new CCircle(temp1);
pc->setAcreage(pc->CalAcr());
pShapes[i] = pc;
break;
case 'T':case 't': //三角形
cin>>temp1>>temp2>>temp3;
pt = new CTriangle(temp1,temp2,temp3);
pt = new CTriangle(temp1,temp2,temp3);
pt->setAcreage(pt->CalAcr());
pShapes[i]= pt;
break;
}
}
if(n == 1) cout<<"共有"<<n<<"種圖形,它是:"<<endl;
else cout<<"共有"<<n<<"種圖形爱只,分別是:"<<endl;
for(i=0;i<n;++i)
{
pShapes[i]->PrintInfo();
delete pShapes[i]; //釋放空間
}
return 0;
}
3
C 6
R 7.6 8.2
T 3 4 5
共有3種圖形皿淋,分別是:
圓。 半徑=6,面積=113.04
矩形恬试。 寬度=7.6,高度=8.2,面積=62.32
三角形窝趣。三條邊分別是:3,4,5,面積=6
多態(tài)的使用
類的成員函數(shù)直接按可以互相調(diào)用。
在普通成員函數(shù)(靜態(tài)成員函數(shù)训柴、構造函數(shù)和析構函數(shù)除外)中調(diào)用其他虛成員函數(shù)也是允許的哑舒,并且是多態(tài)的。
程序6-6 在成員函數(shù)中調(diào)用虛函數(shù)
#include <iostream>
using namespace std;
class CBase
{
public:
void func1() //不是虛函數(shù)
{
cout<<"CBase::func1()"<<endl;
func2(); //在成員函數(shù)中調(diào)用虛函數(shù)
func3();
}
virtual void func2()
{
cout<<"CBase::func2()"<<endl;
}
void func3()
{
cout<<"CBase::func3()"<<endl;
}
};
class CDerived:public CBase
{
public:
virtual void func2()
{
cout<<"CDerived::func2()"<<endl;
}
void func3()
{
cout<<"CDerived::func3()"<<endl;
}
};
int main()
{
CDerived d;
d.func1();
return 0;
}
CBase::func1()
CDerived::func2()
CBase::func3()
程序6-7 在構造函數(shù)與析構函數(shù)中調(diào)用虛函數(shù)
#include <iostream>
using namespace std;
class A
{
public:
virtual void hello()
{
cout<<"A::hello"<<endl;
}
virtual void bye()
{
cout<<"A::bye"<<endl;
}
};
class B:public A
{
public:
virtual void hello()
{
cout<<"B::hello"<<endl;
}
B()
{
hello();
}
~B()
{
bye();
}
};
class C:public B
{
public:
virtual void hello()
{
cout<<"C::hello"<<endl;
}
};
int main()
{
C obj;
return 0;
}
B::hello
A::bye
實現(xiàn)多態(tài)時畦粮,必須滿足的條件是:使用基類指針或引用來調(diào)用基類中聲明的虛函數(shù)。
程序6-8 多態(tài)與非多態(tài)的對比
#include <iostream>
using namespace std;
class A
{
public:
void func1()
{
cout<<"A::func1"<<endl;
}
virtual void func2()
{
cout<<"A::func2"<<endl;
}
};
class B:public A
{
public:
virtual void func1()
{
cout<<"B::func1"<<endl;
}
void func2()
{
cout<<"B::func2"<<endl;
}
};
class C:public B
{
public:
void func1()
{
cout<<"C::func1"<<endl;
}
void func2()
{
cout<<"C::func2"<<endl;
}
};
int main()
{
C obj;
A *pa = &obj;
B *pb = &obj;
pa->func2(); //多態(tài)
pa->func1(); //不是多態(tài)
pb->func1(); //多態(tài)
return 0;
}
C::func2
A::func1
C::func1
虛析構函數(shù)
聲明虛析構函數(shù)的格式
virtual ~類名();
虛析構函數(shù)沒有返回值類型乖阵,沒有參數(shù)宣赔。
如果一個類的析構函數(shù)是虛函數(shù),則由它派生的所有子類的析構函數(shù)也是虛析構函數(shù)瞪浸。
程序6-9 不使用虛析構函數(shù)的情況
#include <iostream>
using namespace std;
class ABase
{
public:
ABase()
{
cout<<"ABase 構造函數(shù)"<<endl;
}
~ABase()
{
cout<<"ABase 析構函數(shù)"<<endl;
}
};
class Derived:public ABase
{
public:
int w,h;
Derived()
{
cout<<"Derived 構造函數(shù)"<<endl;
w = 4;
h = 7;
}
~Derived()
{
cout<<"Derived 析構函數(shù)"<<endl;
}
};
int main()
{
ABase *p = new Derived();
delete p;
return 0;
}
ABase 構造函數(shù)
Derived 構造函數(shù)
ABase 析構函數(shù)
改寫儒将,將基類析構函數(shù)修改為虛析構函數(shù)。
virtual ~ABase()
{
cout<<"ABase 析構函數(shù)"<<endl;
}
ABase 構造函數(shù)
Derived 構造函數(shù)
Derived 析構函數(shù)
ABase 析構函數(shù)
純虛函數(shù)喝抽象類
純虛函數(shù)
純虛函數(shù)是聲明在基類中的虛函數(shù)对蒲,沒有具體的定義钩蚊,而由各派生類根據(jù)識記需要給出各自的定義贡翘。
聲明純虛函數(shù)的格式
virtual 函數(shù)類型 函數(shù)名(參數(shù)表) = 0;
抽象類
包含純虛函數(shù)的類稱為抽象類。
純虛函數(shù)和函數(shù)體為空的虛函數(shù)的區(qū)別:
- 純虛函數(shù)沒有函數(shù)體砰逻,而空的虛函數(shù)的函數(shù)體為空鸣驱。
- 純虛函數(shù)所在的類是抽象類,不能直接進行實例化蝠咆;而空的虛函數(shù)所在的類是可以實例化的踊东。
共同特點是:都可以派生出新的類,然后在新類中給出虛函數(shù)的實現(xiàn)刚操,而且這種新的實現(xiàn)具有多態(tài)特征闸翅。
程序6-10 抽象類示例
#include <iostream>
using namespace std;
class A
{
private:
int a;
public:
virtual void print() = 0;
void func1()
{
cout<<"A_func1"<<endl;
}
};
class B:public A
{
public:
void print();
void func1()
{
cout<<"B_func1"<<endl;
}
};
void B::print()
{
cout<<"B_print"<<endl;
}
int main()
{
// A a; //錯誤,抽象類不能實例化
// A *p = new A; //錯誤菊霜,不能創(chuàng)建類A的實例
// A b[2]; //錯誤坚冀,不能聲明抽象類的數(shù)組
A *pa; //正確,可以聲明抽象類的指針
A *pb = new B;
pb->print();
B b;
A *pc = &b;
pc->func1();
return 0;
}
B_print
A_func1
虛基類
定義虛基類的格式
class 派生類名 : virtual 派生方式 基類名
{
派生類體
};
程序6-11 虛基類
#include <iostream>
using namespace std;
class A
{
public:
int a;
void showa()
{
cout<<"a="<<a<<endl;
}
};
class B:virtual public A
{
public:
int b;
};
class C:virtual public A
{
public:
int c;
};
class D:public B,public C
{
//派生類D的兩個基類B鉴逞、C具有共同的基類A记某,
//采用了虛繼承,從而使類D的對象中只包含著類A的1個實例
public:
int d;
};
int main()
{
D Dobj;
Dobj.a = 11;
Dobj.b = 22;
Dobj.showa();
cout<<"Dobj.b="<<Dobj.b<<endl;
}
a=11
Dobj.b=22