6

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

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 {}语法实际上是无效的。不幸的是,无论我是否指定,C4505 仍然出现inline

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

4

4 回答 4

3

这是 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

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

于 2013-08-01T15:36:18.420 回答
0

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

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

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

于 2012-09-26T01:16:19.977 回答
0

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

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

于 2013-08-01T16:15:32.147 回答
0

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

比如这段代码

于 2012-09-26T00:38:38.147 回答