欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

构造函数和析构函数可以是虚函数吗?

程序员文章站 2022-03-15 16:32:55
...

构造函数可以是虚函数吗,为什么

答:不可以

因为对象中的虚函数表指针是在构造函数初始化列表阶段才初始化的。

C++之父 Bjarne Stroustrup 在《The C++ Programming Language》里说:
To construct an object, a constructor needs the exact type of the object it is to create. Consequently, a constructor cannot be virtual. Furthermore, a constructor is not quite an ordinary function, In particular, it interacts with memory management in ways ordinary member functions don’t. Consequently, you cannot have a pointer to a constructor.
— From 《The C++ Progamming Language》15.6.2
翻译一下:
要构造一个对象,构造函数需要它要创建的对象的确切类型。 因此,构造函数不能是虚函数的。 此外,构造函数不是一个普通的函数,特别是它以普通成员函数所不具备的方式与内存管理进行交互。 因此,你不可能获得构造函数的指针。

C++对象的构建分为两步,首先分配一块内存,其次调用它的构造函数。这时如果它的构造函数是虚函数,那就要通过对象中的虚函数表指针来调用,而这个虚函数表是在构造函数初始化列表阶段才初始化的。

析构函数可以是虚函数吗,为什么

答:可以

来看下面这段代码:

#include <iostream>

using namespace std;

class Person {
public:
	~Person()
	{
		cout << "~Person()" << endl;
	}
};
class Student : public Person {
public:
	~Student()
	{
		cout << "~Student()" << endl;
	}
};

void test()
{
	Person *p = new Student;
	delete p;
}

int main()
{
	test();
	return 0;
}

运行后输出:
构造函数和析构函数可以是虚函数吗?
由此我们发现:当基类指针指向派生类对象时,析构函数只会调用基类的析构函数,从而造成内存泄漏。

原因:在调用普通函数时,在编译期间已经确定了它所要调用的函数(静态绑定),因为p是Person*类型,因此只会调用基类的析构函数。

解决方法:把基类的析构函数定义成虚函数。

#include <iostream>

using namespace std;

class Person {
public:
	virtual ~Person() // 将基类的析构函数定义成虚函数
	{
		cout << "~Person()" << endl;
	}
};
class Student : public Person {
public:
	~Student()
	{
		cout << "~Student()" << endl;
	}
};

void test()
{
	Person *p = new Student;
	delete p;
}

int main()
{
	test();
	return 0;
}

运行结果:
构造函数和析构函数可以是虚函数吗?
我们发现:先调用了派生类的析构函数,后调用了基类的析构函数。

原因:当我们把基类析构函数定义为虚函数时,在调用析构函数时,会在程序运行期间根据指向的对象类型到它的虚函数表中找到对应的虚函数(动态绑定),此时找到的是派生类的析构函数,因此调用该析构函数;而调用派生类析构函数之后会再调用基类的析构函数,因此不会导致内存泄漏。