VC杂记
多態(tài)性的類型
程序設計的多態(tài)性有兩種形式:編譯時的多態(tài)性和運行時的多態(tài)性。
多態(tài)性的實現(xiàn)與聯(lián)編有關。將一個函數(shù)的調用與其相應的函數(shù)體代碼相鏈接的過程,稱為函數(shù)聯(lián)編。C++中有兩種類型的聯(lián)編:靜態(tài)聯(lián)編和動態(tài)聯(lián)編。
編譯時的多態(tài)性是通過靜態(tài)聯(lián)編來實現(xiàn)的。靜態(tài)聯(lián)編是指在調用同名函數(shù)(即重載函數(shù))時,編譯器將根據(jù)調用時所使用的實際參數(shù)個數(shù)、類型的不同確定應該調用哪一個函數(shù)的實現(xiàn),它是在程序編譯階段就確定下來的多態(tài)性。靜態(tài)聯(lián)編通過使用重載(overload)機制來獲得,重載機制包括函數(shù)重載和運算符重載。
運行時的多態(tài)性是通過動態(tài)聯(lián)編來實現(xiàn)的。動態(tài)聯(lián)編是在程序的運行階段根據(jù)當時的情況,確定應該調用哪個同名函數(shù)的實現(xiàn)。動態(tài)聯(lián)編主要通過繼承與虛函數(shù)兩者結合來實現(xiàn)的。
靜態(tài)聯(lián)編與動態(tài)聯(lián)編各有優(yōu)缺點。靜態(tài)聯(lián)編的代碼鏈接工作是在編譯時完成的,所以運行時不需要額外時間來做這些工作,因此靜態(tài)聯(lián)編的代碼效率較高;而動態(tài)聯(lián)編將函數(shù)調用的鏈接時間后移到代碼執(zhí)行的時候,這必然使函數(shù)調用時間增加,效率降低,但大大增強了語言的靈活性。
多態(tài)的前提條件
???????? 必須存在一個繼承體系結構。
???????? 繼承體系結構中的一些類必須具有同名的virtual成員函數(shù)(virtual是關鍵字)。
???????? 至少有一個基類類型的指針或基類類型的引用,這個指針或引用可用來對virtual成員函數(shù)進行調用。
注意:
基類類型的指針可以指向任何基類對象或派生類對象,反過來派生類類型的指針是不可以的基類的對象的。
函數(shù)重載
如果兩個以上的函數(shù)取相同的函數(shù)名稱,但是形參個數(shù)或者類型不同時,編譯器會根據(jù)實參和形參的類型和個數(shù)選擇最匹配的函數(shù),自動確定調用哪個函數(shù),這就是函數(shù)的重載。
注意?①函數(shù)重栽時,函數(shù)聲明必須能夠互相區(qū)別,即函數(shù)的參數(shù)個數(shù)或參數(shù)類型必須有所不同,如果只有函數(shù)的返回值不同,則不能區(qū)分重栽函數(shù);②函數(shù)功能表面相類似,本質上有區(qū)別的函數(shù),最好不要重載;③不同參數(shù)傳遞方式無法區(qū)別重載函數(shù)。例如,int add(int a, int b)和int add(int &a, int &b)這兩個函數(shù)不能作為重載函數(shù),否則編譯時會出錯。
操作符重載
下列操作符不能被重載:
???????? 成員選擇符(.)、成員對象選擇符(.*)、域解析操作符(::)和條件操作符(?:)
賦值操作符(=)不能被派生類所繼承。
???????? 下標操作符[]、賦值操作符=、函數(shù)調用操作符()和指針操作符->必須以類的成員函數(shù)的形式進行重載。
賦值操作符的重載:
???????? 拷貝構造函數(shù)和賦值操作符都是用來拷貝一個類的對象給另一個同類型的對象??截悩嬙旌瘮?shù)將一個對象拷貝到另一個新的對象;賦值操作符將一個對象拷貝到另一個已經(jīng)存在的對象。如果類的設計者沒有提供拷貝構造函數(shù),也沒有重載賦值操作符,編譯器將會為這個類提供一個拷貝構造函數(shù)和一個賦值操作符。需要注意的問題就是和拷貝構造函數(shù)同樣的問題,編譯器提供的運作機制的特點以及引起的問題。
重載不能改變操作符的優(yōu)先級和語法。
???????? 如果一個內(nèi)建操作符是一元的,那么所有對它的重載仍是一元的。如果一個內(nèi)建操作符是二元的,那么所有對它的重載仍是二元的。
虛函數(shù)
基類的在派生類中仍然是虛函數(shù),無論其前面是否有關鍵字virtual,編譯器都將視它們?yōu)樘摵瘮?shù)。虛函數(shù)和普通成員函數(shù)一樣,在派生類中虛成員函數(shù)也可以從基類繼承。構造函數(shù)不能是虛成員函數(shù),但析構函數(shù)可以是虛成員函數(shù),而且一般都把一個類的析構函數(shù)設計成虛成員函數(shù)。只有非靜態(tài)成員函數(shù)才可以是虛函數(shù);只有對象成員函數(shù)才可以是虛函數(shù)。
注意:
???????? 基類中被聲明了為虛函數(shù)的成員函數(shù),在被繼承以后的派生類當中即使不被顯式的聲明為虛函數(shù),它也自動地被認為是虛函數(shù)。
???????? 虛函數(shù)在類聲明之外定義,關鍵字virtual僅在函數(shù)聲明時需要,不需在函數(shù)定義中使用virtual關鍵字。
???????? C++僅允許將成員函數(shù)定義為虛函數(shù),頂層函數(shù)不能為虛函數(shù)
在基類中只聲明虛函數(shù)而不給出具體的定義,將它的具體定義放在各派生類中,這種虛函數(shù)稱為純虛函數(shù)(pure virtual function)。通過該基類指針或引用就可以調用所有派生類的虛函數(shù),基類只是用于繼承,僅作為一個接口,具體功能在派生類中實現(xiàn)。聲明了純虛函數(shù)的類,稱為抽象類(abstract class)。
純虛函數(shù)的聲明形式如下:
virtual? 函數(shù)類型?函數(shù)原型(參數(shù)表)=0;
將虛函數(shù)的原型設置為0,該虛函數(shù)即為純虛函數(shù)。
抽象類的定義
只能用于別的類的基類,而本身不能直接創(chuàng)建對象的類稱為抽象基類。帶有純虛函數(shù)的基類是抽象類。它的主要作用是通過抽象類為一個類族建立一個公共的接口,使其能夠更有效地發(fā)揮多態(tài)性。抽象類聲明了一族派生類共同操作的通用語義,而接口的完整實現(xiàn)——純虛函數(shù)的實現(xiàn)體,要由派生類自己給出。
純虛函數(shù)的例子:
#include <iostream.h>?
class shape
{
protected:
double x,y;
public:
void set (double I, double j) {x=I; y=j;}
virtual void area()=0;//聲明純虛函數(shù)
}; //抽象類shape定義結束
class triangle: public shape
{
public:
void area()
{
cout <<"Triangle S=1/2*"<<x<<"*"<<y<<"="<<0.5*x*y<<""n";
} // 派生類重新定義虛函數(shù)
};
class rectangle: public shape
{
public:
void area()
{
cout <<"Rectangle S=" <<x<<"*"<<y<<"="<<x*y<<""n";
}//派生類重新定義虛函數(shù)
}; //派生類rectangle定義結束
void main()
{
shape *p;
triangle t;
rectangle r;
p=&t;
p->set(5.1,10);
p->area();
p=&r;
p->set(5.1,10);
p->area();
}
采用虛函數(shù)的優(yōu)點有:
①可以使程序簡單易懂;
②使程序模塊間的獨立性加強,增加了程序的易維護性;
③提高了程序中“信息隱藏”的等級。類的封裝本身是私有成員的隱藏,而在基類和派生類之間虛函數(shù)的設置,實際上是以虛函數(shù)作為對外接口,它隱藏的是在各個派生類中虛函數(shù)內(nèi)容各異的不同實現(xiàn);
④提高了“軟件重用”的等級。虛函數(shù)是一類簇對外提供的接口,它實質上是一種接口重用。
通過基類指針來訪問
一般情況下,指向一種類型對象的指針不允許指向另一種類型的對象。然而在具有層次關系的類結構中,指向基類對象的指針可以指向該基類的公有派生類對象(反向則不成立)。通過調用基類指針所指向的虛函數(shù)(指針指向哪一個派生類就調用哪一個派生類的虛函數(shù)),最終實現(xiàn)運行時的多態(tài)性。
純虛函數(shù)的定義
在基類中只聲明虛函數(shù)而不給出具體的定義,將它的具體定義放在各派生類中,這種虛函數(shù)稱為純虛函數(shù)(pure virtual function)。通過該基類指針或引用就可以調用所有派生類的虛函數(shù),基類只是用于繼承,僅作為一個接口,具體功能在派生類中實現(xiàn)。聲明了純虛函數(shù)的類,稱為抽象類(abstract class)。
純虛函數(shù)的聲明形式如下:
virtual? 函數(shù)類型?函數(shù)原型(參數(shù)表)=0;
將虛函數(shù)的原型設置為0,該虛函數(shù)即為純虛函數(shù)。
繼承
類型兼容規(guī)則:
一個公有派生類的對象在使用上可以被當作基類的對象,反之則禁止。具體表現(xiàn)在:
派生類的對象可以被賦值給基類對象。
派生類的對象可以初始化基類的引用。
指向基類的指針也可以指向派生類。
通過基類對象名、指針只能使用從基類繼承的成員。
繼承機制下的構造函數(shù):
??? 當創(chuàng)建一個派生類對象時,基類的構造函數(shù)被自動調用,用來對派生類對象中的基類部分進行初始化,并完成其他一些相關事務。如果派生類定義了自己的構造函數(shù),則由該構造函數(shù)負責對象中派生類添加部分的初始化工作。
繼承機制下的析構函數(shù):
在類的層次結構當中,構造函數(shù)按基類到派生類的次序執(zhí)行,析構函數(shù)則按照派生類到基類的次序執(zhí)行,因此,析構函數(shù)的執(zhí)行次序和構造函數(shù)的執(zhí)行次序是相反的。
基類成員的訪問控制:
表3.1?繼承成員訪問控制規(guī)則
| 繼承訪問控制 | 基類成員訪問控制 | 在派生類中的訪問控制 |
| public | public | public |
| protected | protected | |
| private | 不可訪問 | |
| protected | public | protected |
| protected | protected | |
| private | 不可訪問 | |
| private | public | private |
| protected | private | |
| private | 不可訪問 |
靜態(tài)數(shù)據(jù)成員及成員函數(shù)
class Task
{
public:
?????? // …
private:
?????? static int?sm_nCount;
?????? // …
};
int Task:: sm_nCount = 1000;
?? 特點:
??? 1. 屬于一個類所共有;
2. static數(shù)據(jù)成員在類聲明的內(nèi)部聲明,它必須在任何程序塊之外被定義;
3. static數(shù)據(jù)成員不會影響該類及其對象的sizeof。
class Task
{
public:
?????? static int getCount() const { return sm_nCount; }
?????? // …
private:
?????? static int?sm_nCount;
?????? // …
};
???????? 靜態(tài)成員函數(shù)只能訪問其他的static成員,包括數(shù)據(jù)成員和成員函數(shù),不能訪問非static數(shù)據(jù)成員和非static成員函數(shù)。
內(nèi)聯(lián)函數(shù)聲明與使用
???? 聲明時使用關鍵字 inline。
???? 編譯時在調用處用函數(shù)體進行替換,節(jié)省了參數(shù)傳遞、控制轉移等開銷。
???? 注意:
–?內(nèi)聯(lián)函數(shù)體內(nèi)不能有循環(huán)語句和switch語句。
–?內(nèi)聯(lián)函數(shù)的聲明必須出現(xiàn)在內(nèi)聯(lián)函數(shù)第一次被調用之前。
–?對內(nèi)聯(lián)函數(shù)不能進行異常接口聲明。
拷貝構造函數(shù)
拷貝構造函數(shù)是一種特殊的構造函數(shù),其形參為本類的對象引用。
class 類名
{?
public :
?????? 類名(形參); //構造函數(shù)
?????? 類名(類名 &對象名);//拷貝構造函數(shù)
?????????? ...
};
類名:: 類名(類名 &對象名)//拷貝構造函數(shù)的實現(xiàn)
{???
函數(shù)體???
}
當用類的一個對象去初始化該類的另一個對象時系統(tǒng)自動調用拷貝構造函數(shù)實現(xiàn)拷貝賦值。
int main()
{?
?? Point A(1,2);
?? Point B(A);????? //拷貝構造函數(shù)被調用
?? cout << B.GetX() << endl;
}
若函數(shù)的形參為類對象,調用函數(shù)時,實參賦值給形參,系統(tǒng)自動調用拷貝構造函數(shù)。例如:
void fun1(Point p)
{??
??? cout<<p.GetX()<<endl;
}
int main()
{??
??? Point A(1,2);
??? fun1(A);??????? //調用拷貝構造函數(shù)
}?? ??
當函數(shù)的返回值是類對象時,系統(tǒng)自動調用拷貝構造函數(shù)。例如:
Point fun2()
{???
???? Point A(1,2);
???? return A;?????? //調用拷貝構造函數(shù)
}
int main()
{
???? Point B;
???? B = fun2();
}
拷貝構造函數(shù)小結
???????? 特點:
???? ???1. 拷貝構造函數(shù)創(chuàng)建一個新的對象,它是一個已有對象的拷貝;
?????????????????? 2. 拷貝構造函數(shù)可以有多個參數(shù);
?????????????????? 3. 如果類的設計者沒有提供拷貝構造函數(shù),編譯器會自動生成一個。
何時需要定義自己的拷貝構造函數(shù):通常,如果一個類包含指向動態(tài)存儲空間指針類型的數(shù)據(jù)成員,則就應為這個類設計拷貝構造函數(shù)。轉載于:https://www.cnblogs.com/Wolf-PL/archive/2007/12/26/1016011.html
總結
- 上一篇: 用“已知”的办法解决“未知”的办法---
- 下一篇: C++:函数参数不确定时用cstdarg