构造函数,析构函数和虚函数相关问题

构造函数

要了解这个话题,首先我们给出一段代码:

#include<iostream>
using namespace std;
class Animal{
    int x;
public:
    Animal(int x){
        this->x = x;
    }
    virtual void eat(){
        cout<<"eat"<<endl;
    }
};
class Herbivore:public Animal{
    int y;
public:
    Herbivore(int x, int y):Animal(x){
        this->y = y;
    }
    void eat(){
        cout<<"eat grass"<<endl;
    }
};
void print(Animal &a){
    int num = sizeof(a)/ sizeof(int);
    int *p = (int*)&a;
    while(num){
        printf("%x\n",*p);
        ++p;
        --num;
    }
};
int main(){
    Animal a(1);
    Herbivore h(1,2);
    cout<<"Show the Animal Object...\n";
    print(a);
    cout<<"Show the Herbivore object...\n";
    print(h);
    system("pause");
    return 0;
}

运行结果如下:
test-object

包含虚函数的对象有以下的结构:
object structure

当基类中的数据成员是private类型的,继承类的对象无法直接访问数据成员,但是它又必须创建所有的数据对象。从上面的对象的结构,我们也发现,基类对象在上,继承类的对象依次在下面。这也表明了如下的结论:

构造函数的调用次序是沿着类的生成过程进行的。因为只有保证了底层的正确,才能进一步构造。这样的生成顺序也是为了vptr的设置考虑的。因最终的vptr是most-derived的那个类的virtual table的地址,所以,从下往上的生成过程会用上层的vptr不断覆盖底层的,使得最终得到正确的vptr.

如果在构造函数中调用虚函数会和在普通函数中有和区别呢?

众所周知,构造函数是产生对象的函数,如果在构造函数中调用虚函数,那么只能调用base类中的虚函数,并且在前面加上相应的namespace.如果什么都不加,则默认是该对象中的虚函数。而普通成员函数中调用虚函数,则很有可能是一个继承当前类的一个类中的成员函数。

下面用程序来进行说明:

#include<iostream>
using namespace std;
class Animal{
    int x;
public:
    Animal(int x){
        this->x = x;
    }
    virtual void eat(){
        cout<<"eat "<<x<<endl;
    }
    void description(){
        eat();
    }
};
class Herbivore:public Animal{
    int y;
public:
    Herbivore(int x, int y):Animal(x){
        this->y = y;
    }
    void eat(){
        cout<<"eat grass"<<endl;
        cout<<y<<endl;
    }
};

int main(){
    Herbivore h(1,2);
    h.description();
    system("pause");
    return 0;
}

Animal类中的成员函数description有一个引用类型的参数,并且,该参数调用了一个虚函数,因此需要在运行时决定其类型。

析构函数

构造函数是不能被声明为虚函数的。因为构造函数是piece-by-piece的模式来构建对象的。那么析构函数可以么?
下面来看一个例子:

#include<iostream>
using namespace std;
class Base1{
public:
    ~Base1(){
        cout<<"~Base1()\n";
    }
};

class Derived1:public Base1{
public:
    ~Derived1(){
        cout<<"Derived1\n";
    }
};

class Base2{
public:
    virtual ~Base2(){
        cout<<"~Base2()\n";
    }
};

class Derived2:public Base2{
public:
    ~Derived2(){
        cout<<"Derived2"<<endl;
    }
};

int main(){
    Base1 *bp = new Derived1;
    delete bp;
    Base2 *b2p = new Derived2;
    delete b2p;
    system("pause");
    return 0;
}

运行结果如下:
virtual destructor

从运行结果可得出下面的结论:
如果将析构函数声明为虚函数的话,可以保证按照从下往上的调用次序进行析构;否则,就只调用基类的析构函数。所以,将析构函数设置为虚函数则是必要的。
产生这种现象的原因是,当用基类的指针或引用来操作派生类的对象时,如果析构函数不设置为虚函数,那么它对于基类来说是不可见的,因此无法正确的调用。
至于链式的析构,则是在析构函数内部实现的。和构造函数的思想类似。也是唯一使用这种链式机制的两类函数。

reference
thinking in c++

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值