1、多态的实现需要利用虚函数的动态联编,即在运行时进行加载,进行动态调用~
#include <iostream>
using namespace std;
class Base{
public:
Base( int val ){ value = val; }
void print(){ cout << "Base" << " " << value << endl; } // virtual void print(){ cout << "Base" << " " << value << endl; }
protected:
int value;
};
class B : public Base{
public:
B( int val ) : Base( val ){}
void print(){ cout << "B" << endl; }
};
int main(){
Base *base;
Base base1(0);
base = &base1;
base->print();
B b(2);
base = &b;
base->print();
}
输出结果为 Base0 Base2;
分析: 因为print()函数前面没有virtual,普通成员函数采用的是静态联编(标识符和存储地址联系在一起),所以在编译链接生成可执行代码的过程已经把对象识别了,而非在运行的时候进行动态加载,所以输出的是Base2,而非B
修改:将Base类中的print()函数修改为virtual类型
2、析构函数必须申明为virtual类型
如果一个基类的析构函数被说明为虚析构函数,则它的派生类中的析构函数也是虚析构函数,不管它是否使用了关键字virtual进行说明。
说明虚析构函数的目的在于在使用delete运算符删除一个对象时,能保析构函数被正确地执行。因为设置虚析构函数后,可以采用动态联编方式选择析构函数。
#include <iostream.h>
class A
{
public:
virtual ~A() { cout<<"A::~A() Called.\n"; }
};
class B : public A
{
public:
B(int i) { buf = new char[i]; }
virtual ~B()
{
delete [] buf;
cout<<"B::~B() Called.\n";
}
private:
char * buf;
};
void fun(A *a)
{
delete a;
}
void main()
{
A *a = new B(15);
fun(a);
}
执行该程序输出如下结果:
B::~B() Called.
A::~A() Called.
如果类A中的析构函数不用虚函数,则输出结果如下:
A::~A() Called.
当说明基类的析构函数是虚函数时,调用fun(a)函数,执行下述语句:
delete a;
由于执行delete语句时自动调用析构函数,采用动态联编,调用它基类的析构函数,所以输出上述结果。
当不说明基类的析构函数为虚函数时,delete隐含着对析构函数的调用,故产生
A::~A() Called.
的结果。 此部分转载自:http://topic.csdn.net/u/20110302/23/089640d3-0eb4-4063-a664-55edb0643802.html
追述:virtual函数的实现细节
a、欲实现virtual函数,对象必须携带某些信息,主要用来在运行期决定哪一个virtual函数会被调用,而这份信息通常是由一个所谓的vptr(virtual table pointer)指针指出。vptr指向一个由函数指针构成的数组,成为vtbl(virtual table),每一个带有virtual函数的class都有一个相应的vtbl。当对象调用某一virtual函数,实际被调用的函数取决于该对象的vptr所指向的那个vtbl——编译器在其中寻找适当的函数指针
b、如果一个point class内涵virtual函数,其对象体积会增加:32-bits计算机体系结构中将占用64bits(为了存放两个ints)至96bits(再加上vptr),64-bits的同理。因此,为Point添加一个vptr会增加其对象达50~100%,而且c++的Point对象不再和其他语言(如C)内的相同声明有着一样额结构(因为其他语言中的对应物并没有vptr),也因此不再具有可移植性
c、因此,武端的将所有的classes的析构函数声明为virtual,就像从未声明他们为virtual一样,都是错误的。只有当class内含有至少一个virtual函数,才为他声明virtual析构函数
总结:
a、带有多态特性的base类应该声明一个virtual西沟函数。如果class带有任何的virtual函数,它就应该拥有一个virtual析构函数
b、class的设计目的如果不是作为base calss使用的话,或不是为了具备多态性,就不该声明virtual析构函数
3、多继承和虚基类
a、由于c++的多继承性,导致了在派生类继承基类的过程中,会出现某些基类成员访问的不唯一现象,也即二义性
b、多继承的路径上,可能出现公共基类的,从而产生多个访问实例,为了避免产生这种现象,可以将公用的基类声明称virtual类型,从而之产生单个基类的实例
#include <iostream>
using namespace std;
class Base{
public:
Base( int val ){ value = val; }
virtual void print(){ cout << "Base" << " " << value << endl; }
protected:
int value;
};
class A : virtual public Base{ // 虚基类
public:
A( int val ) : Base( val ){}
void print(){ cout << "A" << endl; }
};
class B : public Base{
public:
B( int val ) : Base( val ){}
void print(){ cout << "B" << endl; }
};
class C : public Base{
public:
C( int val ) : Base( val ){}
void print(){ cout << "C" << endl; }
};
class D : virtual public Base, A{ // 多继承,Base虚基类避免多次重复继承
public:
D( int val ) : Base( val ), A( val ){}
void print(){ cout << "D" << endl; }
};
class E : public B, C{
public:
E( int val ) : B( val ), C( val ){}
void print(){ cout << "E" << endl; }
};
int main(){
Base *base;
Base base1(0);
base = &base1;
base->print();
A a(1);
base = &a;
base->print();
B b(2);
base = &b;
base->print();
C c(3);
base = & c;
base->print();
D d(4);
base = &d;
base->print();
E e(4);
e.print();
system("pause");
return 0;
}
输出结果: Base0 A B C D E
分析:上述如果在类D继承Base 和 A 的时候不把 Base声明成virtual类型,会报错
说明:1、构造函数调用的过程中,virtual类型的基类将被首先调用
2、如果该基类也是派生类,也将遵循从基类开始构造的原则,然后是派生类
3、同一层次的虚基类按照他们声明的次序来进行调用