基类中受保护的非虚拟析构函数
Protected non-virtual destructor in the base class
我试图理解虚拟析构函数。以下是此页面的复制粘贴 何时使用虚拟析构函数?
在这里,您会注意到我没有将 Base 的析构函数声明为 虚拟。现在,让我们看一下以下代码片段:
Base *b = new Derived(); // use b delete b; // Here's the problem!
[...]如果要防止通过基类指针删除实例,可以使基类析构函数受保护且非虚拟;这样,编译器将不允许在基类指针上调用 delete。
我不明白为什么通过拥有受保护的非虚拟基类析构函数来阻止删除。编译器不认为我们试图从基类对象调用delete
吗?protected
与这有什么关系?
C++ 标准对delete
有这样的说法(第 5.3.5p10 节):
对释放函数和析构函数都进行了访问和歧义控制 (12.4, 12.5)。
因此,只有有权访问析构函数的代码才能使用 delete
。 由于析构函数是protected
的,这意味着没有人可以在 Base*
类型的指针上调用 delete
。 只有子类可以使用析构函数(唯一会的是子类自己的析构函数,作为子对象销毁过程的一部分)。
当然,子类应该制作自己的析构函数public
,允许您通过子类类型删除对象(假设这是正确的实际类型)。
注意:实际上,Base
的其他成员可以执行delete (Base*)p;
,因为他们具有访问权限。 但是C++假设使用此构造的人不会这样做 - C++访问控制仅提供对类外代码的指导。
delete b;
有效地执行b->~Base(); deallocate(b);
。如果析构函数不可访问,则第一部分 - 调用析构函数 - 将无法编译(与调用任何其他不可访问的方法失败的方式相同)。
根据我的理解(基于此页面),我们想在基类中使用非虚拟和受保护析构函数的唯一情况如下:
#include <iostream>
struct unary_function {
protected:
~unary_function() {
std::cout << "unary_function" << std::endl;
}
};
struct IsOdd : public unary_function {
public:
bool operator()(int number) {
return (number % 2 != 0);
}
};
void f(unary_function *f) {
// compile error
// delete f;
}
int main() {
// unary_function *a = new IsOdd;
// delete a;
IsOdd *a = new IsOdd;
delete a;
getchar();
return 0;
}
因此,您只能执行以下操作:
IsOdd *a = new IsOdd;
delete a;
或
IsOdd c;
从不这些:
unary_function *a = new IsOdd;
delete a;
因此,对于非虚拟受保护析构函数,当您尝试使用它时,编译器会给出错误
void f(unary_function *f) {
delete f;
// this function couldn't get compiled because of this delete.
// you would have to use the derived class as the parameter
}
类的受保护方法和变量(我们称之为Base
)只能由派生类访问。因此,如果在派生类外部的 Base
类型的指针上调用 delete
,它将尝试调用 Base::~Base()
(Base 的析构函数),但由于它受到保护,因此无法调用它,从而导致编译错误。
根据规范,基类的析构函数必须仅声明为受保护的和非虚拟的(不允许通过Base
指针删除派生对象),或者声明为公共和虚拟的(允许通过Base
指针安全删除派生对象)。
如果析构函数被声明为公共和非虚拟,则在删除指向派生类的 Base 类型的指针时,它会导致未定义的行为。
两个选项:
- 非虚拟受保护析构函数 - 无法删除指向
Derived
Base
指针:
class Base {
public:
Base() {
std::cout << "Base ctor called.n";
}
protected:
~Base() {
std::cout << "Base dtor called.n";
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived ctor called.n";
}
~Derived() {
std::cout << "Derived dtor called.n";
}
};
Base *foo = new Derived;
delete foo; // compilation error
。正如你的问题中提到的。
- 公共虚拟析构函数 - 允许删除指向
Derived
对象的类型Base
的指针。首先调用Derived
析构函数,然后调用Base
析构函数:
class Base {
public:
Base() {
std::cout << "Base ctor called.n";
}
virtual ~Base() {
std::cout << "Base dtor called.n";
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived ctor called.n";
}
~Derived() override {
std::cout << "Derived dtor called.n";
}
};
Base *foo = new Derived;
delete foo;
输出:
Base ctor called.
Derived ctor called.
Derived dtor called.
Base dtor called.
- 重载 -> shared_ptr 个实例中的箭头运算符<interface>,接口中没有纯虚拟析构函数
- 是否可以使用函数指针调用虚拟析构函数?
- 在没有动态内存的世界中,我是否需要虚拟析构函数?
- 程序永远不会进入虚拟析构函数
- C++ std::vector 中的虚拟析构函数继承
- 哪种方法更适合处理虚拟析构函数?
- 拥有"受保护的非虚拟析构函数"与"受保护虚拟析构构函数"有什么好处
- 带有未解析外部元素的C++虚拟析构函数
- 即使基类和派生类只使用基元数据类型,我是否需要定义虚拟析构函数
- C++切片和虚拟析构函数
- C++虚拟继承、虚拟析构函数和 dynamic_cast<void*>
- 添加虚拟析构函数会使代码大小膨胀
- 应该是虚拟析构函数吗?但是怎么做呢?
- 虚拟析构函数将对象移出 rodata 部分
- 如何将 std::unique_ptr<Parent> 与具有受保护虚拟析构函数的只读父类一起使用
- DIRECTX9 中自定义顶点的虚拟析构函数
- 为什么缺少虚拟析构函数不会导致内存泄漏?
- std::unique_ptr 在虚拟析构函数上重置 SIGABRT
- C++11 中默认纯虚拟析构函数的正确放置
- 在派生类中重写哪个基类的虚拟析构函数