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;
  }
};

この方法で 2 つの異なるインターフェイスを実装しようとすると、問題が発生します。

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 などの他のコンパイラは警告を発しません。構文 "... = 0 {...}" は、C++03 標準セクション 10.4.2 によると (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

非インラインの方法でデストラクタを定義しようとしましたか? おそらく警告はそれに関連しています。

このコードなど。

于 2012-09-26T00:38:38.147 に答える
0

仮想関数をインラインに定義することはできません。

インラインがコンパイルされているためです。仮想は実行中です。

于 2013-08-01T16:15:32.147 に答える