【问题标题】:Virtual default destructor definition in source file vs in header file源文件与头文件中的虚拟默认析构函数定义
【发布时间】:2019-11-08 21:59:13
【问题描述】:

今天,在一次讨论中,一位同事告诉我,如果在源文件而不是头文件中定义默认的析构函数,会有所不同。我不记得谈话的细节(主要是因为我听不懂他的论点),但他说了类似的话:

如果 dtor 在头文件中默认设置,则智能指针可能会调用错误的 dtor。这与智能指针持有的类型的前向声明、dll 边界以及标头中的默认 dtor 被内联的事实有关。

假设我们有一个基类型和一个派生类型,它们是某个 dll 的 API 的一部分。

Base.hpp

struct Base
{
    virtual ~Base() = default;
};

派生的.hpp

#define DLL_API __declspec(dllexport) 

struct Derived : Base
{
    // some arbitrary content

    DLL_API Derived();
    DLL_API virtual ~Derived() = default; // (1)
};

如果我以任何方式使用 Derived,无论是在 dll 内部还是外部,如果 (1) 处的 dtor 默认为内联(如上面的代码中所示),是否会有所不同? ) 或者如果它在源文件中是默认的,如下所示?

派生的.hpp

#define DLL_API __declspec(dllexport) 

struct Derived : Base
{
    // some arbitrary content

    DLL_API Derived();
    DLL_API virtual ~Derived();
};

派生的.cpp

Derived::~Derived() = default;

是否存在Derived 的行为不同的场景,如果 dtor 在源文件中默认为内联?

【问题讨论】:

  • 在类内部,析构函数是默认的,而在外部,它是用户提供的。
  • @Jarod42 我明白你的意思,但这不会影响程序的行为,或者是吗?
  • 如果内联定义了析构函数,编译器就有更多机会实际内联它。在某些情况下,动态分配的内存会导致跨 DLL 边界的问题(例如,内存在 DLL 中分配,并在其外部释放),因为可能会使用不同的释放器函数。我手边没有参考资料,但记得读过一些声明,即内联定义的析构函数或不内联会导致使用不同的释放器,但我持怀疑态度,因为我也不记得看到任何证据证明这种声明。
  • @Peter 我猜如果双方有不同的标准实现,或者如果一方覆盖 operator new/delete,这可能会发生。

标签: c++ visual-c++ c++17


【解决方案1】:

在 C++20 中显式默认的析构函数是 constexpr(如果可能)。这是语言上唯一的显着差异。也是inline,但这不重要。

【讨论】:

  • 这就是问题所在。是否存在内联导致意外副作用的情况?
  • @Timo:它不应该在 C++17 中。在 C++20 中,inline 在模块中具有规范作用,但即便如此,我认为它不会有意义地影响“空”析构函数。
猜你喜欢
  • 2010-10-24
  • 1970-01-01
  • 2017-04-21
  • 2014-01-30
  • 1970-01-01
  • 2011-09-03
  • 2016-03-26
  • 2021-12-09
  • 2012-07-14
相关资源
最近更新 更多