参考资料:http://www.programfan.com/article/2782.html
作为通常的原则,如果一个类定义了虚函数,那么它的析构函数就应当是virtual的。因为定义了虚函数则隐含着:这个类会被继承,并且会通过基类的指针指向子类对象,从而得到多态性。”,因此基类的析构函数是否为虚将决定子类的对象是否被析构。
虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数。
class A { public: virtual voidfoo() { cout << "A::foo() is called"<< endl;} }; class B: public A { public: virtual voidfoo() { cout << "B::foo() is called"<< endl;} };
那么,在使用的时候,我们可以:
A * a = new B(); 父类类型的指针指向子类类型的对象,从而父类调用子类函数
a->foo(); // 在这里,a虽然是指向A的指针,但是被调用的函数(foo)却是B的!
这个例子是虚函数的一个典型应用,通过这个例子,也许你就对虚函数有了一些概念。它虚就虚在所谓“推迟联编”或者“动态联编”上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为“虚”函数。
class A { public: virtual voidfoo(); }; class B: public A { virtual voidfoo(); }; void bar() { A a; a.foo(); // A::foo()被调用 } void bar(A * a) { a->foo(); //若a指向A的实例,则A的foo被调用,若a指向B的实例,则B的foo被调用。 多态 }
#include <iostream> using namespace std; struct A { A(){cout<<"A::()"<<endl;} virtual ~A(){cout<<"~A()\n";} }; struct B: public A { B(){cout<<"B::()"<<endl;} ~B(){cout<<"~B()\n";} };
不用virtual 的几种情况:
1、作为非公有基类。仅作为 private base class 使用的 class不需要使用虚拟析构函数
2、不作为接口使用的基类。
3. 如果你可以保证这个类不被public继承(private/protected继承的话,在非friend函数/类中就无法用基类指针指向派生类了)
4. 如果它的所有派生类(包括派生类的派生类)的析构函数都是trivial的(这里的trivial指的是在程序员的层次什么事也不做)
5. 如果不需要用基类的指针指向派生类的对象
在这五种情况下,不把析构函数声明为virtual都是可以的,何况效率会高一些——但前提是你得保证前提的成立——不过这些保证常常是很难100%的:谁能保证别人在派生你的类的时候,析构函数是trivial的,或者别人不用你提供的基类的指针指向派生类对象?这些常常是很难得到保证的。
声明基类的析构函数为virtual并非总是为了防止memory leak 另外这也只是作为一般的原则(基类中有虚函数则把其析构函数声明为virtual)。如果你的析构函数什么事也不作,从效果上来说,不声明为virtual也无妨
overload重载:不同的参数列表,在静态编译的时候已经确定了。
override覆盖/重写:同样的参数列表,实现多态。在执行时动态联编
2.2 纯虚函数
如下声明表示一个函数为纯虚函数:
class A
{
public:
virtual voidfoo()=0; //=0标志一个虚函数为纯虚函数
};
一个函数声明为纯虚后,纯虚函数的意思是:我是一个抽象类!不要把我实例化!不能被实例化,只能被子类覆盖。纯虚函数用来规范派生类的行为,实际上就是所谓的“接口”。它告诉使用者,我的派生类都会有这个函数。