【问题标题】:Protected non-virtual destructor in the base class基类中受保护的非虚拟析构函数
【发布时间】:2013-11-03 22:58:46
【问题描述】:

我正在尝试理解虚拟析构函数。以下是本页复制粘贴When to use virtual destructors?

在这里,你会注意到我没有声明 Base 的析构函数是 虚拟的。现在,我们来看看下面的sn-p:

Base *b = new Derived(); // use b 
delete b; // Here's the problem!

[...] 如果要防止通过基类指针删除实例,可以使基类析构函数受保护且非虚;这样做,编译器不会让你在基类指针上调用 delete。

我不明白为什么使用受保护的非虚拟基类析构函数来防止删除。编译器不认为我们正在尝试从基类对象调用delete 吗? protected 和这有什么关系?

【问题讨论】:

    标签: c++ protected virtual-destructor


    【解决方案1】:

    C++ 标准对delete 有这样的说法(第 5.3.5p10 节):

    对释放函数和析构函数(12.4、12.5)都进行了访问和歧义控制。

    因此,只有有权访问析构函数的代码才能使用delete。由于析构函数是protected,这意味着没有人可以在Base* 类型的指针上调用delete。只有子类可以使用析构函数(唯一可以使用的是子类自己的析构函数,作为子对象销毁过程的一部分)。

    当然,子类应该有自己的析构函数public,允许你通过子类类型删除对象(假设是正确的实际类型)。

    注意:实际上,Base 的其他成员可以执行delete (Base*)p;,因为他们有权访问。但是 C++ 假定使用这种结构的人不会这样做——C++ 访问控制只为您的类之外的代码提供指导。

    【讨论】:

      【解决方案2】:

      delete b; 有效地执行b->~Base(); deallocate(b);。第一部分 - 调用析构函数 - 如果析构函数不可访问(与调用任何其他不可访问方法失败的方式相同),则将无法编译。

      【讨论】:

      • “功能”,拜托。它不是virtual,也不是像方法一样调度。
      • @BenVoigt:我不太明白“方法”一词在应用于虚拟成员函数时比非虚拟成员函数更贴切。我必须承认我没有意识到这种差异。我可以理解 C++ 中根本没有方法,只有成员函数的反对意见(尽管我认为它可能过于迂腐)。但我不明白这个特殊的划分。
      • @BenVoigt:权威不亚于Wikipedia 将“方法”定义为“与类关联的子例程(或过程)”,甚至谈到“虚拟方法”和“非虚拟方法”。我休息一下。
      • 没有方法,只有成员函数。但是虚成员函数可以方法一样被调度。 (“方法”在OOP理论中定义,肯定涉及到调度。想想“多方法”的扩展情况。)
      • @BenVoigt:Java 有静态方法。那些是“派遣”的吗?我怀疑,即使曾经按照您坚持的思路对“方法”一词进行了严格的定义,但无论好坏,口语使用都明显偏离了这样的定义。
      【解决方案3】:

      据我了解(基于此page),我们想在基类中使用非虚拟和受保护的析构函数的唯一情况如下:

      #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 
      }
      

      【讨论】:

        猜你喜欢
        • 2020-03-06
        • 2023-03-23
        • 2016-01-22
        • 2012-11-23
        • 2012-11-06
        • 2013-11-01
        • 2012-02-16
        • 2014-02-23
        • 2017-09-03
        相关资源
        最近更新 更多