多態的前提條件部分整理
- 子類繼承
- 父類指針或者引用
- 虛函數(virtual <類名><函數名>(參數))
在純面嚮對象語言中,所有的函數都是多態的,而像C++這樣的混合語言,函數既可以是多態的也可以是非多態的,這要由綁定的實際是編譯時刻還是運行時刻來決定
//本類爲多態前提條件的案例
#include <iostream>
using namespace std;
class TradesPerson
{ public:
virtual void sayHi()
{ cout<<"hi."<<endl; }
};
class Tinker : public TradesPerson
{ public:
virtual void sayHi()
{ cout<<"I tinker."<<endl; }
};
class Tailor : public TradesPerson
{ public:
virtual void sayHi()
{ cout<<"I tailor."<<endl; }
};
int main()
{ TradesPerson* p;
int which;
do {
cout<<"1 == TradesPerson, 2 == Tinker, 3 == Tailor";
cin>>which;
} while (which < 1 || which > 3);
switch (which)
{ case 1 : p = new TradesPerson; break;
case 2 : p = new Tinker; break;
case 3 : p = new Tailor; break;
}
p->sayHi(); //動態綁定
//此處用來實施對sayHi的調用
delete p;
return 0;
}
基類型的指針數組,用隨機數決定生成那個對象!
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
class TradesPerson
{ public:
virtual void sayHi() {cout<<"Just hi."<<endl;}
};
class Tinker : public TradesPerson
{ public:
virtual void sayHi() {cout<<"Hi, I tinker."<<endl;}
};
class Tailor : public TradesPerson
{ public:
virtual void sayHi() {cout<<"Hi, I tailor."<<endl;}
};
int main() {
srand( (unsigned) time( NULL ) ); //Sets a random starting point.
TradesPerson* ptrs[10];
unsigned which , i ;
for (i=0; i<10; i++) {
which = 1 + rand() % 3;
switch (which) {
case 1 : ptrs[i] = new TradesPerson; break;
case 2 : ptrs[i] = new Tinker; break;
case 3 : ptrs[i] = new Tailor; break;
}
}
for (i=0; i<10; i++) {
ptrs[i] -> sayHi();
delete ptrs[ i ];
}
return 0;
}
當聲明瞭基類的一個成員函數爲虛函數的時候,即使該成員函數沒有在派生類中被顯示的聲明爲虛函數,但是它所在的派生類中也將自動成爲虛函數。
如果虛函數在類聲明之外定義的,關鍵字vitual僅僅在聲明的時候需要,在定義的時候可以不寫。
注意:C++僅僅允許將成員函數定義爲虛函數,不能將頂層函數定義爲虛函數。
virtual void f(); //error
int main(){
.......
}
以下是個例子
#include <iostream>
using namespace std;
class High
{ protected:
float H;
public:
High(float h)
{ H=h;}
virtual void Show() //在基類中定義虛函數Show()
{ cout<<"High="<<H<<endl;}
};
class Cuboid:public High
{ private:
float Length,Width;
public:
Cuboid(float l=0,float w=0,float h=0):High(h)
{ Length=l; Width=w;}
virtual void Show() //在長方體派生類中定義虛函數Show()
{ cout<<"Length="<<Length<<'\t';
cout<<"Width="<<Width<<'\t';
cout<<"High="<<H<<'\n';
cout<<"Cuboid Volume="<<Length*Width*H<<endl;
}
};
class Cylinder:public High
{ private:
float R;
public:
Cylinder(float r=0,float h=0):High(h)
{R=r;}
virtual void Show() //在圓柱體派生類中定義虛函數Show()
{ cout<<"Radius="<<R<<'\t';
cout<<"High="<<H<<'\n';
cout<<"Cylinder Volume="<<R*R*3.1415*H<<endl;
}
};
void main(void)
{ High h(10),*p;
Cuboid cu(10,10,10);
Cylinder cy(10,10);
h.Show();
cu.Show();
cy.Show();
p=&h;
p->Show();
p=&cu;
p->Show();
p=&cy;
p->Show();
}
虛函數也是可以繼承的!!!
class A{
public:
virtual void sayHello(){cout<<"Hello"<<end1}
}
class B : public A{
}
int main(){
B b;
b.sayHello();
}
//結果輸出Hello
構造函數不能是虛函數,但是析構函數可以是虛函數。
class A {
public:
virtual A();//Error
virtual A(int);//Error
virtual ~A();//Right
virtual void m();
}
原因在於虛擬調用是一種能在給定信息下不完全的情況下的工作機制。虛擬允許調用某個函數,對於這個函數,僅僅知道他的接口,而不知道具體的類型。但是要創建一個對象,必須要擁有完全的信息,需要知道建立的對象的具體類型。
虛析構函數確實很有必要的,看下這段代碼
#include <iostream>
using namespace std;
class A {
public:
A() { cout<<endl<<"A() firing"<<endl; //*p = new char[5];
};
~A() { cout<<"~A() firing"<<endl; //delete [ ] p;
}
private:
char* p;
};
class Z : public A
{ public:
Z() {
cout<<"Z() firing"<<endl;
//q = new char[5000];
};
~Z() {
cout<<"~Z() firing"<<endl;
//delete [ ] q;
}
private:
char* q;
};
void f()
{ A* ptr;
ptr = new Z();
//delete ptr;
}
int main()
{ for (unsigned i = 0; i<3; i++) f();
return 0;
}
如果說析構函數不是虛成員函數的話,那麼編譯器實施靜態綁定!!!
結果會造成內存泄漏!!!