【问题标题】:Why must a base class destructor be accessible only when a custom constructor is declared?为什么只有在声明自定义构造函数时才能访问基类析构函数?
【发布时间】:2012-02-25 17:59:59
【问题描述】:

Comeau、g++ (ideone) 和 EDG 接受以下代码而不进行诊断。 Visual C++ 编译成功,但出现警告 C4624。

class indestructible_base
{
  ~indestructible_base();
};

class T : indestructible_base
{
public:
  //T() {}
};

int main(void) { new T(); }

取消注释构造函数,它不再编译。

也许这是规则,如果构造函数内部发生异常,则必须销毁子对象?看起来很奇怪,因为主体是空的并且不会导致异常。即便如此,添加一个异常规范以保证不会引发异常(throw()noexcept),这没有任何区别。

为什么用户声明的构造函数需要访问基类析构函数,而自动生成的构造函数不需要?

这个问题的灵感来自:Preventing a Destructor from Running in C++

【问题讨论】:

  • FWIW,Clang 在 C++0x 模式下的两种情况下都拒绝程序,但在 c++98 模式下的行为类似于 g++。
  • 当你将 ~indestructible_base() 声明为 public 时,编译器不会抱怨。
  • 这可能与异常有关。如果有用户定义的构造函数,则不能保证它不会抛出。如果它抛出,则需要销毁基础子对象。但我可能错了。 - 如果你有一个非平凡的成员,例如 std::string,即使没有用户定义的构造函数,它也不会编译。 (同样,这可能会抛出编译器生成的构造函数。)
  • 绝对是错误的标签:可访问性这不是。
  • @Norman:抱歉,正确的标签是access-modifiers

标签: c++ inheritance destructor access-modifiers language-lawyer


【解决方案1】:

好吧,如果自动生成的构造函数调用了一个可能抛出的构造函数,那么it will give the same access error

#include <string>

class indestructible_base
{
  ~indestructible_base();
  std::string s; // <------ this may throw
};

class T : indestructible_base
{
public:
  //T() {}
};

int main(void) { new T(); }

所以我想例外是答案。在 ANSI ISO IEC 14882 中,唯一的 noexcept(true) 字符串构造函数是移动构造函数。我相信this 应该编译,但 ideone 说不。

【讨论】:

  • 但是如果允许默认的非抛出构造函数,是否也应该允许显式的throw()noexcept 构造函数?
  • FWIW、Microsoft DevStudio v10 和 Microsoft DevStudio v11 开发预览版可以在定义或不定义 T() 的情况下编译此代码。
【解决方案2】:

我怀疑这可能是特定于编译器的行为。这是我的理论:

因为(在这种特殊情况下)隐式定义的 T() 是一个 普通 构造函数(在标准的 12.1(5) 中定义),编译器甚至不会尝试生成T() 的主体。由于没有 ctor 主体,因此在“构造”期间不会产生任何异常(实际上没有异常),因此无需生成 dtor 调用,因此无需生成 dtor 主体, 才发现基类的 dtor 是私有的。

但是一旦 T() 变得不平凡(即使它仍然是隐式定义的),就必须生成一个 ctor 主体,并且您会收到错误。像向具有用户定义的构造函数的类 T 添加成员这样简单的事情会使隐式定义的 T() 变得不平凡。

一个单独但相关的问题是new T() 不会生成 dtor 调用(因为您在任何地方都没有对应的delete)。相反,如果我只是在您的代码中将new T() 替换为T dummy,那么我会从gcc 获得以下信息,这表明它现在正在对dtor 可访问性进行全面检查(由于必须生成dtor 调用):

test.cpp: In destructor 'T::~T()':
test.cpp:3: error: 'indestructible_base::~indestructible_base()' is private
test.cpp:7: error: within this context
test.cpp: In function 'int main()':
test.cpp:12: note: synthesized method 'T::~T()' first required here
test.cpp:12: warning: unused variable 'dummy'

【讨论】:

  • 对。但是noexcept用户定义的构造函数也不能抛出,那为什么会失败呢?
  • @BenVoigt。看来jjlin是正确的。再看 12.1.5。似乎与异常没有任何关系,而是构造函数是否“微不足道”。为了使构造函数变得平凡,所有成员和基都必须具有平凡的构造函数(+其他要求)。 (indestructible_base 也可以是成员。)如果构造函数不重要,则不会生成析构函数。此外,声明构造函数使其变得不平凡(在 c++11 中使用 = default 时除外)。代码很好,因为没有人调用那个(不存在的)析构函数。还要注意 jjlin 关于删除的评论;
  • 查看 gcc 源代码,我得到的印象是没有检查 ctor 是否抛出。当然,源代码相当复杂,所以我不能肯定,但可能只是编译器编写者采取了保守的方法,如果他们必须生成一个ctor,就生成一个dtor。
猜你喜欢
  • 2018-07-03
  • 2019-01-21
  • 2011-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-05
相关资源
最近更新 更多