【问题标题】:Compiler generated destructors and warnings in GCC and MSVC编译器在 GCC 和 MSVC 中生成析构函数和警告
【发布时间】:2015-09-15 16:13:13
【问题描述】:

我正在努力清除在使用提升警告时出现在 GCC 和 MSVC 上的一些警告。我在 MSVC 下捕获了一个 “类具有虚拟函数,但析构函数不是此类的虚拟实例可能无法正确销毁”。当启用适当的警告时,我在 GCC 和 Clang 下收到类似的警告。我正在尝试了解投诉的内容。

有问题的类有没有析构函数(虚拟或其他;由人类编写)。它的基类也是如此。该类具有堆栈分配的成员对象,但没有分配new。班级是template。最后,有时,层次结构中的类被标记为“no vtable”(但不是这种情况)。

如果我添加一个空的虚拟析构函数,则会清除警告。但鉴于上述一些限制,我想确保我没有遗漏一些不明显的东西。

以下是我与编译器和警告相关的问题:

  • 为什么编译器生成的析构函数不是虚拟的?
  • 为什么编译器生成的析构函数不够用?
  • 我提供的空dtor和编译器提供的默认dtor有什么区别?
  • 模板会影响编译器生成的内容吗?
  • “no vtable”会影响编译器生成的内容吗?

如果上面的答案是有效的“我需要学习使用我的工具”,那么我很乐意提供空的 dtor 来使用分析工具。 (我认为声称这些工具不如程序员聪明是非常不诚实的,所以禁用它们。坏人可能会一直笑到银行采摘开发人员无法打扰的低挂果实......) .


这是带有警告标记的类之一。

template <class T>
class DL_FixedBasePrecomputationImpl : public DL_FixedBasePrecomputation<T>
{
public:
    typedef T Element;

    DL_FixedBasePrecomputationImpl() : m_windowSize(0) {}       

    // DL_FixedBasePrecomputation
    bool IsInitialized() const
        {return !m_bases.empty();}
    void SetBase(const DL_GroupPrecomputation<Element> &group, const Element &base);
    const Element & GetBase(const DL_GroupPrecomputation<Element> &group) const
        {return group.NeedConversions() ? m_base : m_bases[0];}
    void Precompute(const DL_GroupPrecomputation<Element> &group, unsigned int maxExpBits, unsigned int storage);
    void Load(const DL_GroupPrecomputation<Element> &group, BufferedTransformation &storedPrecomputation);
    void Save(const DL_GroupPrecomputation<Element> &group, BufferedTransformation &storedPrecomputation) const;
    Element Exponentiate(const DL_GroupPrecomputation<Element> &group, const Integer &exponent) const;
    Element CascadeExponentiate(const DL_GroupPrecomputation<Element> &group, const Integer &exponent, const DL_FixedBasePrecomputation<Element> &pc2, const Integer &exponent2) const;

private:
    void PrepareCascade(const DL_GroupPrecomputation<Element> &group, std::vector<BaseAndExponent<Element> > &eb, const Integer &exponent) const;

    Element m_base;
    unsigned int m_windowSize;
    Integer m_exponentBase;         // what base to represent the exponent in
    std::vector<Element> m_bases;   // precalculated bases
};

以下是一些相关问题:

【问题讨论】:

  • 顺便说一句,这是一个非常糟糕的界面。您的类应始终在构造函数中初始化。如果你曾经需要 Initialize() 或 IsInitialized() 之类的东西,那你就做错了。
  • @Puppy - 我认为它们实际上是内部使用的私有类,并且它们提供延迟初始化。也可以在没有组参数的情况下构造一个(例如,在用户选择的字段上生成新密钥时)。我认为该设计来自类似于 MFC 的东西,其中构造了较低的 C++ 对象,并初始化了 Windows 特定的东西。但是,它就是这样......

标签: c++ visual-c++ gcc warnings virtual-destructor


【解决方案1】:

有问题的类没有析构函数(虚拟或其他)。

有趣的事实:C++ 中的所有对象都有一个析构函数。很简单,如果你不定义一个,编译器会生成它,对于原语,它是一个空操作。

为什么编译器生成的析构函数不是虚拟的?

因为人们抱怨说,如果每个 C++ 程序中的每个类都有一个虚拟析构函数,会对他们的性能产生不利影响。

为什么编译器生成的析构函数不够用?

这完全足够了——销毁它所生成的类。销毁它的派生类(如果有的话)是它不应该服务的目的。

我提供的空 dtor 和 编译器提供的默认dtor?

如果您没有将其定义为virtual,那么空 dtor 将禁止优化(有时),仅此而已。否则,区别是显而易见的——你的是virtual(另外)。某些模板可能要求您的类可以简单地破坏,但这并不常见。

模板会影响编译器生成的内容吗?

没有。

“no vtable”会影响编译器生成的内容吗?

没有。

【讨论】:

  • 我认为您可以使用virtual ~X() = default; 拥有一个虚拟的、非用户提供的析构函数,对吧?当然,所有派生但不是用户声明的析构函数都是这样的。
  • 我认为你可以有一个虚拟的默认析构函数,但是默认它并没有什么区别,因为它不是微不足道的。
  • 是的,这很重要,但至少它不能由用户提供......如果有人关心的话:-)
  • “有趣的事实:C++ 中的所有对象都有一个析构函数....” - 是的,当然。我的意思是我们没有提供一个。很抱歉造成混乱。
  • @jww:是的,尽管假设没有人会继承它是一个非常危险的假设。此外,如果它从具有 vvirtual dtor 的基类继承,那么 dtor 无论如何都是虚拟的。
【解决方案2】:

编译器生成的析构函数不是虚拟的,除非类有一个带有虚拟析构函数的基类。

原因是“如果不使用则零开销”规则 - 虚函数引入了成本,除非有某种原因使析构函数是虚拟的(例如,基类具有虚拟析构函数,或者程序员声明了一个)析构函数不会是虚拟的。

编译器提供的空析构函数和程序员提供的空析构函数没有区别,除非编译器生成的编译器是非虚拟的并且程序员将析构函数声明为虚拟的。

由于模板是一种编译时机制,它们会影响编译器生成和 CAN 生成的内容。这是一个太笼统的问题,无法更具体地回答。

您的最后一个问题是特定于编译器的。一般来说,对于使用 vtables 的编译器,没有 vtable 的类不能具有虚函数,并且 - 如果一个类将任何函数声明为 virtual 或者它从具有任何虚函数的类继承 - 它将获得一个 vtable。在某些情况下(多重继承),该类可能有多个 vtable。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-17
  • 2016-07-30
  • 1970-01-01
  • 2011-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多