英文:
C++: Is a Virtual Destructor needed for stack allocated objects?
问题
我是C++的新手,想知道在下面的程序中是否需要使用虚析构函数:
#include <iostream>;
class Shape2D {
protected:
int firstDimension;
int secondDimension;
public:
Shape2D(int a, int b) {
firstDimension = a;
secondDimension = b;
}
virtual void getArea() {
std::cout << "Area undefined." << std::endl;
}
};
class Rectangle : public Shape2D {
public:
Rectangle(int length=0, int width=0) : Shape2D(length, width) {
}
void getArea() {
int area = firstDimension * secondDimension;
std::cout << "Area of rectangle: " << area << " sq units." << std::endl;
}
};
int main()
{
Rectangle rct(4, 5); // 在堆栈上分配派生对象
Shape2D *sh = &rct; // 指向派生对象的基指针
sh->getArea();
// 对象在这里超出范围。
}
我在阅读这篇帖子时发现,最受欢迎的答案说,只要我们想要通过其基指针删除new
分配的派生对象时,使用虚析构函数是合适的。然而,在这个上下文中,我们在main
例程中在堆栈上分配了一个派生对象Rectangle
,并且基指针没有被delete
,所以提供虚析构函数是否重要?
英文:
I am new to C++ and would like to know if the use of a virtual destructor is required in the following program:
#include <iostream>
class Shape2D {
protected:
int firstDimension;
int secondDimension;
public:
Shape2D(int a, int b) {
firstDimension = a;
secondDimension = b;
}
virtual void getArea() {
std::cout << "Area undefined." << std::endl;
}
};
class Rectangle : public Shape2D {
public:
Rectangle(int length=0, int width=0) : Shape2D(length, width) {
}
void getArea() {
int area = firstDimension*secondDimension;
std::cout << "Area of rectangle: " << area << " sq units." << std::endl;
}
};
int main()
{
Rectangle rct(4, 5); // stack allocated derived object
Shape2D *sh = &rct; // base pointer to derived object
sh->getArea();
// object goes out of scope here.
}
I was reading this post and the most upvoted answer said that the use of a virtual destructor is suitable whenever we want to delete a derived object allocated by new
through its base pointer. However, in this context, we are allocating a derived object Rectangle
on the stack in the main
routine and the base pointer is not being delete
-ed so would it matter if a virtual destructor is provided?
答案1
得分: 2
由于派生对象 rct 的实例是在堆栈上而不是使用 new 进行动态分配,因此在基类中不需要虚析构函数。但这是特定于你的代码,而不是一般规则。
虚析构函数的目的是确保通过基类指针删除派生对象时调用派生类的析构函数。这在派生类析构函数需要执行额外工作时很重要,比如释放资源,例如关闭文件、释放内存等。
一般认为,如果基类打算用作多态基类,并且预期会动态分配派生对象,最好提供虚析构函数。还有其他选择,比如将非虚析构函数标记为私有或受保护,这将导致在尝试通过基类删除时出现编译错误。
如果你打算通过基类删除,则有必要有虚析构函数以避免未定义行为。
根据 C++ 标准(ISO/IEC 14882:2017),通过具有非虚析构函数的基类指针删除对象是未定义行为。具体来说,在第 5.3.5 节中,“删除”表述:
“如果要删除的对象的静态类型与其动态类型不同,则静态类型必须是要删除的对象的动态类型的基类,并且静态类型必须具有虚析构函数,否则行为是未定义的。”
如果你知道你的代码不会由第三方扩展,那么可以考虑使用 std::variant 或类似的构造(如果可用)来采用另一种方法。
英文:
Since the instance of the derived object rct is on the stack and not dynamically allocated using new, there is no need for a virtual destructor in the base. This is specific to your exact code though and not a general rule.
The purpose of a virtual destructor is to make sure that the destructor of the derived class is called when a derived object is deleted through a base class pointer. This is important when the derived class destructor has to do extra work like releasing resources, e.g. closing files, freeing memory, etc.
It is generally considered good practice to provide a virtual destructor in a base class if it is intended to be used as a polymorphic base class, and derived objects are expected to be allocated dynamically. There are other options like marking you non-virtual destructor as private or protected which will result in compile errors when you try to delete through the base.
If you intended to delete via the base class then it necessary to have a virtual destructor to avoid undefined behavior.
C++ Standard (ISO/IEC 14882:2017), deleting an object through a pointer to a base class with a non-virtual destructor is undefined behavior. Specifically, section 5.3.5, "Delete" states:
"If the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined."
If you know that your code will not be extended by 3rd party's then you might consider the alternative approach using std::variant or similar construct if available to you.
答案2
得分: 1
不需要虚析构函数,但也不需要公共析构函数。在这种情况下,最好将公共析构函数移除(将其改为受保护的)。
protected:
~Shape2D() = default;
<details>
<summary>英文:</summary>
A virtual destructor is not required, but neither is a public destructor. It is a good practice in this case to remove the public destructor (by making it protected).
protected:
~Shape2D() = default;
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论