【从 C 向 C++ 进阶】- 类 - 23. 构造、析构与虚函数

从 C 向 C++ 进阶系列导航


1. 构造、析构与虚函数

可否想过,构造函数和析构虚函数可以声明为虚函数吗?答案是,构造函数不能声明为虚函数,而析构函数可以声明为虚函数。

虚函数表指针的初始化是发生在构造函数完成之后的,因此,把构造函数编译为虚函数是无意义的。编译时会直接报错。

当类作为父类时,应声明析构函数为虚函数。因为对于堆上的类对象,使用关键字 delete 释放堆空间时,假设父类的析构函数不是虚函数,此时只会调用接收对象的指针类型的析构函数,子类很可能没有被正常地析构;如果声明为虚函数,则可以先调用子类的析构函数再调用父类的析构函数,即为正确的析构顺序。

  • 实验:
class Base_A
{
public:
    ~Base_A()
    {
        cout << "in Base_A" << endl;
    }  
};

class Base_B
{
public:
    virtual ~Base_B()
    {
        cout << "in Base_B" << endl;
    }  
};

class Derived_A : public Base_A
{
public:
    virtual ~Derived_A()
    {
        cout << "in Derived_A" << endl;
    }  
};

class Derived_B : public Base_B
{
public:
    virtual ~Derived_B()
    {
        cout << "in Derived_B" << endl;
    }  
};

int main()
{
	Base_A* p_A = new(Derived_A);
	delete p_A;				// in Base_A
	
	Base_B* p_B = new(Derived_B);
	delete p_B;				// in Derived_B
							// in Base_B    
    return 0;
}

2. 构造、析构中的多态行为

可否想过,构造函数和析构虚函数中是否可以发生多态行为呢?答案是,构造函数与析构函数都不可以发生多态行为。

由于虚函数表指针的初始化发生在构造函数完成之后,因此在构造函数中是无法构成多态行为的,所有想发生多态行为的函数调用都会直接调用本类的虚函数。

而对于析构函数,假设允许其存在多态行为,那么在调用父类的析构函数时,根据多态行为很可能会通过子类的成员函数访问到子类的成员变量,但此时子类应该是先于父类被析构了,因此这种行为是存在很大的风险的。因此,编译器在析构函数中发现调用到虚函数时,会强制转换直接调用本类的虚函数,即不再产生访问虚函数表的动作。

  • 实验:
class Base
{
public:
    Base()
    {		
		Base* p = this;
		p->Print();
    } 
	virtual ~Base()
	{
		Base* p = this;
		p->Print(); 
	}	
	virtual	void Print()
	{
		cout << "in Base" << endl;
	}
};

class Derived : public Base
{
public:
    virtual ~Derived()
    {
       	Base* p = this;
		p->Print(); 
    }  
	virtual	void Print()
	{
		cout << "in Derived" << endl;
	}
};


int main()
{
	Base* p = new(Derived);		// int Base
								
	delete p;				    // int Derived	
	                            // in Base
  
    return 0;
}

以上实验中,析构函数的多态行为会被替换为直接的成员函数调用,即p->Print();会直接替换为Print();

发布了60 篇原创文章 · 获赞 36 · 访问量 5934

猜你喜欢

转载自blog.csdn.net/qq_35692077/article/details/99762743
今日推荐