【问题标题】:Pure virtual inheritance, multiple inheritance, and C4505纯虚继承、多重继承、C4505
【发布时间】:2012-09-26 00:12:14
【问题描述】:

所以我有一个没有抽象方法的抽象基类。为了强制执行抽象,我将(非平凡的)析构函数声明为纯虚拟:

class AbstractClass
{
public:
  AbstractClass()
  {
    std::wcout << L"AbstractClass::AbstractClass()" << std::endl;
  }
  virtual ~AbstractClass() = 0
  {
    std::wcout << L"AbstractClass::~AbstractClass()" << std::endl;
  }
};

class ConcreteClass : public AbstractClass
{
public:
  ConcreteClass()
  {
    std::wcout << L"ConcreteClass::ConcreteClass()" << std::endl;
  }
  virtual ~ConcreteClass()
  {
    std::wcout << L"ConcreteClass::~ConcreteClass()" << std::endl;
  }
};

这会按预期构建和工作;简单定义 ConcreteClass 实例的代码块的输出是

抽象类::抽象类() 具体类::具体类() ConcreteClass::~ConcreteClass() AbstractClass::~AbstractClass()

现在,当我从另一个用作接口类的类派生 AbstractClass 时,它本身具有(微不足道的)虚拟析构函数(纯或其他),它仍然有效:

class IAlpha
{
public:
  virtual ~IAlpha() = 0 {}
};

class AbstractClass : public IAlpha
{
public:
  AbstractClass()
  {
    std::wcout << L"AbstractClass::AbstractClass()" << std::endl;
  }
  virtual ~AbstractClass() = 0
  {
    std::wcout << L"AbstractClass::~AbstractClass()" << std::endl;
  }
};

class ConcreteClass : public AbstractClass
{
public:
  ConcreteClass()
  {
    std::wcout << L"ConcreteClass::ConcreteClass()" << std::endl;
  }
  virtual ~ConcreteClass()
  {
    std::wcout << L"ConcreteClass::~ConcreteClass()" << std::endl;
  }
};

当我尝试以这种方式实现两个不同的接口时,问题就出现了:

class IAlpha
{
public:
  virtual ~IAlpha() = 0 {}
};

class IBeta
{
public:
  virtual ~IBeta() = 0 {}
};

class AbstractClass : public IAlpha, public IBeta
{
public:
  AbstractClass()
  {
    std::wcout << L"AbstractClass::AbstractClass()" << std::endl;
  }
  virtual ~AbstractClass() = 0
  {
    std::wcout << L"AbstractClass::~AbstractClass()" << std::endl;
  }
};

class ConcreteClass : public AbstractClass
{
public:
  ConcreteClass()
  {
    std::wcout << L"ConcreteClass::ConcreteClass()" << std::endl;
  }
  virtual ~ConcreteClass()
  {
    std::wcout << L"ConcreteClass::~ConcreteClass()" << std::endl;
  }
};

此时,在构建时,我收到以下警告:

警告 C4505: 'AbstractClass::~AbstractClass' :
未引用的本地函数已被删除

然而,奇怪的是,输出仍然显示AbstractClass::~AbstractClass() 被调用。

这是 MSVC9 (VS 2008) 中的错误吗?我可以放心地忽略此警告吗?

编辑:我也尝试将纯虚方法定义与类定义分开,显然= 0 {} 语法实际上是无效的。不幸的是,无论我是否指定inline,C4505 仍然出现。

由于我没有找到#pragma 仅针对这些方法发出此警告的方法(警告从代码的其他部分触发),我可能必须从AbstractClass 中删除纯虚拟说明符并依赖使构造函数受到保护。这不是一个理想的解决方案,但它胜过重新架构类层次结构以规避错误警告。

【问题讨论】:

    标签: c++ destructor multiple-inheritance pure-virtual


    【解决方案1】:

    这是 MSVC++ 2010 及更早版本中的错误。代码实际上得到 即使编译器声称已经删除了代码,也会被调用。好像是 在 MSVC++ 2012 中修复。gcc 或 clang 等其他编译器不会发出警告。 正如已经指出的那样,根据 C++03 标准第 10.4.2 节,语法“... = 0 {...}”是非法的(即使 MSVC++ 没有抱怨):

    注意:函数声明不能​​同时提供纯说明符和 定义

    然而,定义一个纯虚析构函数通常不是非法的,第 12.4.7 节指出:

    析构函数可以声明为虚拟(10.3)或纯虚拟(10.4);如果 该类或任何派生类的任何对象都在 程序,应定义析构函数。如果一个类有一个基类 使用虚拟析构函数,其析构函数(无论是用户还是 隐式声明)是虚拟的。

    我禁用警告的方法是在标题中添加以下行:

    #if defined(_MSC_VER) && (_MSC_VER <= 1600)
    #  pragma warning(disable:4505)
    #endif
    

    如果您想在本地禁用警告,那么 #pragma warning( push )#pragma warning( pop ) 可能会有所帮助。见http://msdn.microsoft.com/en-us/library/2c8f766e(v=vs.80).aspx

    由于代码似乎被调用,我认为您可以忽略警告。

    【讨论】:

    • 我很想知道这个错误是在哪里报告的,所以我可以引用它。
    【解决方案2】:

    您是否尝试过以非内联方式定义析构函数?也许警告与此有关。

    this code

    【讨论】:

    • 我尝试了您链接中的代码;即使它像以前一样在运行时正常运行,它也会收到相同的警告。
    【解决方案3】:

    代码不应编译。纯虚函数不能在类定义中定义。将定义移到类之外:

    struct IAlpha {
      virtual ~IAlpha() = 0;
    };
    inline IAlpha::~IAlpha() {}
    // similarly for the rest.
    

    除此之外,代码是正确的,应该可以编译。

    【讨论】:

    • 有趣;我不知道你不能将纯虚拟说明符和方法体结合起来。不幸的是,我也尝试过这种方式,但警告仍然出现(请参阅 jeffmagill 的回答)。
    【解决方案4】:

    您不能将虚函数定义为内联。

    因为内联在编译中。 虚拟在运行时。

    【讨论】:

      猜你喜欢
      • 2014-11-06
      • 2012-01-31
      • 1970-01-01
      • 2017-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-12
      • 2012-10-11
      相关资源
      最近更新 更多