虚函数
构造函数和静态成员函数不能是虚函数
多态的表现形式:
派生类的指针可以赋值给基类指针
通过基类指针用基类和派生类中的同名虚函数时:
- 若该指针指向一个基类的对象,那么被调用是基类的虚函数
- 若该指针指向一个派生类的对象,那么被调用的是派生类的虚函数。
派生类的对象可以赋值给基类的引用
通过基类引用调用基类和派生类中的同名虚函数时:
- 若该引用引用的是一个基类的对象,那么被调用的是基类的虚函数
- 若该引用引用的是一个派生类的对象,那么被调用的是派生类的虚函数
例子。。。。
#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;
B *pb = &b;
D *pd = &d;
E *pe = &e;
pa->print();
pa = pb;
pa->print();
pa = pd;
pa->print();
pa = pe;
pa->print();
return 0;
}
A::Print
B::Print
D::print
E::print
多态的作用:可以增强程序的可扩充性,即:程序需要修改或增加功能的时候,需要改动和增加的代码较少
#include <iostream>
#include <stdlib.h>
#include <math.h>
using namespace std;
class CShape{
public:
virtual double Area()=0;
virtual void PrintInfo()=0;
};
class CRectangle:public CShape{
public:
int w, h;
virtual double Area();
virtual void PrintInfo();
};
class CCircle:public CShape{
public:
int r;
virtual double Area();
virtual void PrintInfo();
};
class CTrinale:public CShape{
public:
int a,b,c;
virtual double Area();
virtual void PrintInfo();
};
double CRectangle::Area() {
return w*h;
}
void CRectangle::PrintInfo() {
cout<<"R:"<<Area()<<endl;
}
double CCircle::Area() {
return 3.14*r*r;
}
void CCircle::PrintInfo() {
cout<<"C:"<<Area()<<endl;
}
double CTrinale::Area() {
int p = (a+b+c)/2;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
void CTrinale::PrintInfo() {
cout<<"T:"<<Area()<<endl;
}
CShape *pShape[100];
int MyCompare(const void *s1, const void *s2){
double a1, a2;
CShape **p1;
CShape **p2;
p1 = (CShape**)s1;
p2 = (CShape**)s2;
a1 = (*p1)->Area();
a2 = (*p2)->Area();
if(a1<a2) return -1;
else if(a2<a1) return 1;
else return 0;
}
int main()
{
int i;int n;
CRectangle *pr;
CTrinale *pt;
CCircle *pc;
cin>>n;
for (i = 0; i < n; ++i) {
char c;
cin>>c;
switch (c){
case 'R':
pr = new CRectangle();
cin>>pr->h>>pr->w;
pShape[i]=pr;
break;
case 'T':
pt = new CTrinale();
cin>>pt->a>>pt->b>>pt->c;
pShape[i]=pt;
break;
case 'C':
pc = new CCircle();
cin>>pc->r;
pShape[i]=pc;
break;
}
}
qsort(pShape, n, sizeof(CShape*), MyCompare);
for (int i = 0; i < n; ++i) {
pShape[i]->PrintInfo();
}
}
下面用sort在补充一次。。。。在此感谢实验室的同门小妹妹,qsort中传入的是一个const void *类型,而sort传入的直接是一个对应的类型
#include <iostream>
#include <stdlib.h>
#include <math.h>
#include <algorithm>
using namespace std;
class CShape{
public:
virtual double Area()=0;
virtual void PrintInfo()=0;
};
class CRectangle:public CShape{
public:
int w, h;
virtual double Area();
virtual void PrintInfo();
};
class CCircle:public CShape{
public:
int r;
virtual double Area();
virtual void PrintInfo();
};
class CTrinale:public CShape{
public:
int a,b,c;
virtual double Area();
virtual void PrintInfo();
};
double CRectangle::Area() {
return w*h;
}
void CRectangle::PrintInfo() {
cout<<"R:"<<Area()<<endl;
}
double CCircle::Area() {
return 3.14*r*r;
}
void CCircle::PrintInfo() {
cout<<"C:"<<Area()<<endl;
}
double CTrinale::Area() {
int p = (a+b+c)/2;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
void CTrinale::PrintInfo() {
cout<<"T:"<<Area()<<endl;
}
CShape *pShape[100];
bool MyCompare( CShape *s1, CShape *s2) {
double a1, a2;
a1 = s1->Area();
a2 = s2->Area();
return a1>a2? true: false;
}
int main()
{
int i;int n;
CRectangle *pr;
CTrinale *pt;
CCircle *pc;
cin>>n;
for (i = 0; i < n; ++i) {
char c;
cin>>c;
switch (c){
case 'R':
pr = new CRectangle();
cin>>pr->h>>pr->w;
pShape[i]=pr;
break;
case 'T':
pt = new CTrinale();
cin>>pt->a>>pt->b>>pt->c;
pShape[i]=pt;
break;
case 'C':
pc = new CCircle();
cin>>pc->r;
pShape[i]=pc;
break;
}
}
sort(pShape, pShape+n, MyCompare);
for (int i = 0; i < n; ++i) {
pShape[i]->PrintInfo();
}
}
在非构造函数,非析够函数的成员函数中调用虚函数,是多态,在构造函数和析够函数中调用虚函数,不是多态
#include <iostream>
using namespace std;
class myclass{
public:
virtual void hello(){cout<<"hello from myclass"<<endl;}
virtual void bye(){cout<<"bye from myclass"<<endl;}
};
class son:public myclass{
public:
void hello(){cout<<"hello form son"<<endl;}
son(){hello();}
~son(){bye();}
};
class grandson:public son{
public:
void hello(){cout<<"hello from grandson"<<endl;}
void bye(){cout<<"bye from grandson"<<endl;}
grandson(){cout<<"constructing grandson"<<endl;}
~grandson(){cout<<"destructing grandson"<<endl;}
};
int main(){
grandson gson;
son *pson;
pson = &gson;
pson->hello();
return 0;
}
hello form son
constructing grandson
hello from grandson
destructing grandson
bye from myclass
在构造函数和析够函数中调用虚函数,不是多态。编译时即可确定,调用的函数是自己的类或基类中定义的函数,不会等到运行时才决定调用自己的还是派生的函数。
多态的实现原理
多态的关键是在于通过基类指针或引用调用一个虚函数时,编译时不确定到底调用的是基类还是派生类的函数,运行时才确定—-这叫动态联编。“动态联编”是如何实现的呢?
多态实现的关键—虚函数表
每一个有虚函数的类(或虚函数的类的派生类)都有个虚函数表,该类的任何对象中都存放着虚函数表的指针。虚函数表中列出了该类的虚函数地址。多出来的4个字节是用来存放虚函数表的地址的。
#include <iostream>
using namespace std;
class A{
public:
virtual void Func(){cout<<"A::Func()"<<endl;}
};
class B:public A{
public:
virtual void Func(){cout<<"B::Func()"<<endl;}
};
int main(){
A a;
A *pa = new B();
pa->Func();
long long *p1 = (long long*) &a;
long long *p2 = (long long*) pa;
*p2 = *p1;
pa->Func();
return 0;
}
虚析够函数
通过基类的指针删除派生类对象时,通常情况下只调用基类的析够函数
但是,删除一个派生类的对象时,应该先调用派生类的析够函数,然后调用基类的析够函数
解决办法:把基类的析够函数声明为virtual
派生类析够函数可以virtual不进行声明
通过基类的指针删除派生类对象时,首先调用派生类的析够函数,然后调用基类析够函数
一般来说,一个类如果定义了虚函数,则应该将析够函数也定义成虚函数。或者,一个类打算作为基类使用,也应该将析够函数定义成虚函数
不允许构造函数为虚函数
#include <iostream>
using namespace std;
class son{
public:
virtual ~son(){cout<<"bye from son"<<endl;}
};
class grandson:public son{
public:
~grandson(){cout<<"bye from grandson"<<endl;}
};
int main()
{
son *pson;
pson = new grandson();
delete pson;
return 0;
}
bye from grandson
bye from son
纯虚函数和抽象类
纯虚函数:没有函数体的虚函数
virtual void Print()=0;
包含纯虚函数的类是抽象类
抽象类只能作为基类来派生新类使用,不能创建抽象类的对象
抽象类的指针和引用可以指向由抽象类派生出来的类的对象
在抽象类的成员函数可以调用纯虚函数,但是在构造函数或析够函数内部不能调用纯虚函数
如果一个类从抽象类派生而来,当且仅当它实现了基类中的所有纯虚函数,它才能够成为非抽象类
#include <iostream>
using namespace std;
class A{
public:
virtual void f()=0;
void g(){this->f();}
};
class B:public A{
public:
void f(){cout<<"B:f()"<<endl;}
};
int main(){
A* a = new B ();
a->g();
return 0;
}
B:f()