基类中受保护的非虚拟析构函数

Protected non-virtual destructor in the base class

本文关键字:虚拟 析构函数 受保护 基类      更新时间:2023-10-16

我试图理解虚拟析构函数。以下是此页面的复制粘贴 何时使用虚拟析构函数?

在这里,您会注意到我没有将 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 类型的指针时,它会导致未定义的行为。

两个选项:

  1. 非虚拟受保护析构函数 - 无法删除指向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

。正如你的问题中提到的。

  1. 公共虚拟析构函数 - 允许删除指向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.