在下面的程序中,派生类析构函数被声明为私有的,那么它是如何被调用的?

10
#include<iostream>

class base
{
   public:
       virtual ~base(){std::cout << "base\n";}
};
class derived : public base
{
   private:
        ~derived(){std::cout << "derived\n";} /* destructor is private */
};
int main()
{
      base *pt= new derived;
      delete pt;
}

上面的程序编译和运行都很好。 私有派生类析构函数怎么被调用的?


你会期望一个编译时错误还是运行时错误? - n. m.
我不会收到任何错误。我很想知道在上面的程序中,析构函数是如何被调用的!(它不应该被调用,因为它已经在私有说明符下声明了。) - Kaustav Ray
如果不应该调用某个函数,但你仍然试图调用它,那么在某个时刻应该发出错误信号。我问你,在哪个时间点你认为发出这样的错误信号是合理和适当的。 - n. m.
我明白您的意思。编译器无法发出错误信号,因为我正在尝试删除一个具有公共析构函数的基类指针。 - Kaustav Ray
没错。不仅如此,这样做是错误的,因为“base”承诺提供析构函数,“derived”不应该违背“base”的承诺。 - n. m.
@KaustavRay,当您想要强制调用者基于接口而不是具体类进行编码时,这将非常有用。 - ZijingWu
2个回答

1
这不仅会发生在析构函数中。
您可以使用私有函数覆盖任何虚拟公共函数。
#include<iostream>

class base
{
   public:
       virtual void x(){std::cout << "base\n";}
};
class derived : public base
{
   private:
        void x(){std::cout << "derived\n"; base::x();}
};
int main()
{
      base *pt= new derived;
      pt->x(); //OK
      //((derived *)pt)->x();  //error: ‘virtual void derived::x()’ is private
      derived *pt2= new derived;
      //pt2->x();  //error: ‘virtual void derived::x()’ is private
      ((base *)pt2)->x(); //OK
}

这样做的优点/缺点是您必须使用指向基类的指针来访问此方法。 此功能是将公共API与定制实现分离的一种方式之一。
换句话说,您的析构函数被调用是因为它在基类中声明为public,并且您通过指向基类的指针调用它。

我正在调用指向基类的指针,但由于在基类中它也被声明为虚拟的,因此应该先调用派生类的析构函数,然后再调用基类的析构函数。 - Kaustav Ray
你的程序会打印出 "derived\nbase\n"。"private" 关键字并不意味着这个函数不能被调用,而是不能通过该类的公共接口来调用。如果在你的情况下写成 "derived *pt= new derived; delete pt;",你将会得到一个错误。 - fsw
当你执行base *pt = new derived;时,你正在执行base pt->destructor = derived->destructor;然后pt->destructor是公共的,当你调用它时,它会在内部调用derived->destructor,这是私有的但从同一类中调用。 - javier_domenech

0

这可能令人惊讶,但如果你仔细想想,它是完全合理的。

编译器看到的是你删除了一个有可访问析构函数的基类指针。因此,它没有理由抱怨你所做的事情。顺便说一下,这是一个虚析构函数,但这也不是什么需要抱怨的事情。

在运行时,虚析构函数被调用。由于指向基类的指针实际上指向的是派生对象,因此需要首先调用派生构造函数。
"但那个构造函数是私有的!" 你说。没错,然而,在运行时没有公共/私有的概念。因此,再次强调,没有任何坏事发生的理由。不会生成错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接