析构函数

析构函数是特殊的类成员函数,它没有返回类型,没有参数,不能随意调用,也没有重载,只有在类对象的生命期结束的时候,由系统自动调用。
有释放内存空间的作用。
虚函数是C++多态的一种表现, 使用虚函数,我们可以灵活的进行动态绑定,当然是以一定的开销为代价。

Tips

C++不支持虚构造函数

A virtual call is a mechanism to get work done given partial information. In particular, “virtual” allows us to call a function knowing only an interfaces and not the exact type of the object. To create an object you need complete information. In particular, you need to know the exact type of what you want to create. Consequently, a “call to a constructor” cannot be virtual.

virtual存在的目的就是为了能够让我们在不知道object类型的情况下去调用接口,但如果你在构造object的时候不知道这个object的类型,那还咋能调用正确类型的接口呢。

通过技巧也可以实现“虚拟构造函数”,比如下面的例子:

  1. struct F { // interface to object creation functions
  2. virtual A* make_an_A() const = 0;
  3. virtual B* make_a_B() const = 0;
  4. };
  5. void user(const F& fac)
  6. {
  7. A* p = fac.make_an_A(); // make an A of the appropriate type
  8. B* q = fac.make_a_B(); // make a B of the appropriate type
  9. // ...
  10. }
  11. struct FX : F {
  12. A* make_an_A() const { return new AX(); } // AX is derived from A
  13. B* make_a_B() const { return new BX(); } // BX is derived from B
  14. };
  15. struct FY : F {
  16. A* make_an_A() const { return new AY(); } // AY is derived from A
  17. B* make_a_B() const { return new BY(); } // BY is derived from B
  18. };
  19. int main()
  20. {
  21. user(FX()); // this user makes AXs and BXs
  22. user(FY()); // this user makes AYs and BYs
  23. // ...
  24. }

关键之处是,user()被完全孤立开了——它对AX,AY这些类一无所知。

构造函数是否可以调用虚函数?

可以,但是,在构造函数中,虚拟机制尚未发生作用,因此override尚未发生。对象的建立过程是:先把基类构造完毕,然后在此基础上构造派生类。

#include<string>
#include<iostream>
using namespace std;

class B {
    public:
    B(const string& ss) { cout << "B constructor\n"; f(ss); }
    virtual void f(const string&) { cout << "B::f\n";}
};

class D : public B {
    public:
    D(const string & ss) :B(ss) { cout << "D constructor\n";}
    void f(const string& ss) { cout << "D::f\n"; s = ss; }
    private:
    string s;
};

int main()
{
    D d("Hello");
}

//Output
B constructor
B::f
D constructor

注意,输出不是D::f 。 究竟发生了什么?f()是在B::B()中调用的。如果构造函数中调用虚函数的规则不是如前文所述那样,而是如一些人希望的那样去调用D::f()。那么因为构造函数D::D()尚未运行,字符串s还未初始化,所以当D::f()试图将参数赋给s时,结果多半是——立马当机。
析构则正相反,遵循从继承类到基类的顺序(拆房子总得从上往下拆吧?),所以其调用虚函数的行为和在构造函数中一样:虚函数此时此刻被绑定到哪里(当然应该是基类啦——因为继承类已经被“拆”了——析构了!),调用的就是哪个函数。

在构造和析构期间不要调用virtual函数,因为这类调用从来不下降至derived class