繁体   English   中英

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

[英]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); 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 的析构函数),但由于它是受保护的,因此无法调用它,从而导致编译错误。

根据规范,基类的析构函数只能声明为 protected 和 non-virtual(不允许通过Base指针删除派生对象),或 public 和 virtual(允许通过Base指针)。

如果析构函数被声明为公共的和非虚拟的,那么如果指向派生类的 Base 类型的指针被删除,则会导致未定义的行为。

两个选项:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM