std :: vector <AbstractClass>を宣言できないのはなぜですか?


88

C#での開発にかなりの時間を費やした後、インターフェイスとして使用する目的で抽象クラスを宣言すると、この抽象クラスのベクトルをインスタンス化して子クラスのインスタンスを格納できないことに気付きました。

#pragma once
#include <iostream>
#include <vector>

using namespace std;

class IFunnyInterface
{
public:
    virtual void IamFunny()  = 0;
};

class FunnyImpl: IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        cout << "<INSERT JOKE HERE>";
    }
};

class FunnyContainer
{
private:
    std::vector <IFunnyInterface> funnyItems;
};

抽象クラスのベクトルを宣言する行により、MSVS2005で次のエラーが発生します。

error C2259: 'IFunnyInterface' : cannot instantiate abstract class

IFunnyInterfaceを次のように置き換えるという明らかな回避策があります。

class IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        throw new std::exception("not implemented");
    }
};

これはC ++に関して許容できる回避策ですか?そうでない場合、これを回避するのに役立つブーストのようなサードパーティのライブラリはありますか?

これを読んでくれてありがとう!

アンソニー

回答:


127

抽象クラスをインスタンス化できないため、抽象クラスのベクトルは機能しません。

ただし、抽象クラスへのポインタのベクトルを使用できます。

std::vector<IFunnyInterface*> ifVec;

これにより、実際にポリモーフィックな動作を使用することもできます。クラスが抽象的でなくても、値で格納すると、オブジェクトのスライスの問題が発生します


5
または、オブジェクトの存続期間を手動で処理したくない場合は、std :: vector <std :: tr1 :: shared_ptr <IFunnyInterface >>を使用できます。
Sergey Teplyakov 2010年

4
またはさらに良いことに、boost :: ptr_vector <>。
ロエル2010年

7
または今、std :: vector <std :: unique_ptr <IFunnyInterface >>。
カズドラゴン

21

抽象クラスのインスタンスを作成できないため、抽象クラスタイプのベクトルを作成することはできません。また、std :: vectorのようなC ++標準ライブラリコンテナは値(つまりインスタンス)を格納します。これを行う場合は、抽象クラス型へのポインターのベクトルを作成する必要があります。

仮想関数(そもそも抽象クラスが必要な理由)はポインターまたは参照を介して呼び出された場合にのみ機能するため、回避策は機能しません。参照のベクトルを作成することもできないため、これがポインターのベクトルを使用する必要がある2番目の理由です。

C ++とC#にはほとんど共通点がないことを理解する必要があります。C ++を学ぶつもりなら、最初から始めることと考えて、KoenigとMooによるAccelerated C ++などの優れた専用C ++チュートリアルを読む必要があります。


投稿への返信に加えて、本を推薦していただきありがとうございます!
blueTrin 2010年

しかし、抽象クラスのベクトルを宣言するとき、抽象クラスを作成するように要求しているのではなく、そのクラスの非抽象サブクラスを保持できるベクトルだけですか?ベクトルコンストラクターに数値を渡さない限り、作成する抽象クラスのインスタンスの数をどのようにして知ることができますか?
ジョナサン。

6

この場合、次のコードも使用できません。

std::vector <IFunnyInterface*> funnyItems;

または

std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;

FunnyImplとIFunnyInterfaceの間にIS関係がなく、プライベート継承のためにFUnnyImplとIFunnyInterfaceの間に暗黙の変換がないためです。

次のようにコードを更新する必要があります。

class IFunnyInterface
{
public:
    virtual void IamFunny()  = 0;
};

class FunnyImpl: public IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        cout << "<INSERT JOKE HERE>";
    }
};

1
ほとんどの人は私が思う私的継承を見ました:)しかしOPをさらに混乱させないでください:)
Roel 2010年

1
うん。特にトピックのスターターのフレーズの後:「C#での開発にかなりの時間を費やした」(プライベート継承がまったくない場合)。
Sergey Teplyakov 2010年

6

従来の代替手段はvector、すでに述べたように、ポインターのを使用することです。

感謝する人のためにBoost、非常に興味深いライブラリが付属しています。Pointer Containersこれは、タスクに完全に適しており、ポインタによって暗示されるさまざまな問題から解放されます。

  • 生涯管理
  • イテレータの二重間接参照

これはvector、パフォーマンスとインターフェイスの両方の点で、スマートポインタよりも大幅に優れていることに注意してください。

さて、3番目の選択肢があります。それは階層を変更することです。ユーザーの絶縁を強化するために、次のパターンが何度も使用されています。

class IClass;

class MyClass
{
public:
  typedef enum { Var1, Var2 } Type;

  explicit MyClass(Type type);

  int foo();
  int bar();

private:
  IClass* m_impl;
};

struct IClass
{
  virtual ~IClass();

  virtual int foo();
  virtual int bar();
};

class MyClass1: public IClass { .. };
class MyClass2: public IClass { .. };

これは非常に簡単でPimplStrategyパターンによって強化されたイディオムのバリエーションです。

もちろん、これは「真の」オブジェクトを直接操作したくない場合にのみ機能し、ディープコピーが含まれます。だからそれはあなたが望むものではないかもしれません。


1
Boostリファレンスとデザインパターンをありがとう
BlueTrin 2010年

2

ベクトルのサイズを変更するには、デフォルトのコンストラクターとクラスのサイズを使用する必要があるため、具体的なものである必要があります。

他の提案どおりにポインタを使用できます。


1

std :: vectorは、タイプを含むようにメモリを割り当てようとします。クラスが純粋に仮想である場合、ベクターは割り当てる必要のあるクラスのサイズを知ることができません。

回避策を使用すると、をコンパイルするvector<IFunnyInterface>ことはできますが、その中のFunnyImplを操作することはできないと思います。たとえば、IFunnyInterface(抽象クラ​​ス)のサイズが20(私は本当にわかりません)で、FunnyImplのサイズが30である場合、メンバーとコードが多いため、30を20のベクトルに適合させようとします。

解決策は、「new」を使用してヒープにメモリを割り当て、ポインタをに格納することです。 vector<IFunnyInterface*>


これが答えだと思いましたが、gfの返信とオブジェクトのスライスを探してください。コンテナ内で何が起こるかを正確に説明しています
BlueTrin 2010年

この答えは何が起こるかを説明しましたが、「スライス」という言葉を使用しなかったので、この答えは正しいです。ptrのベクトルを使用する場合、スライスは発生しません。それが、そもそもptrsを使用することの要点です。
ロエル2010年

-2

この本当に悲しい制限の根本的な原因は、コンストラクターが仮想化できないという事実だと思います。そのコンパイラは、コンパイル時の時間を知らずにオブジェクトをコピーするコードを生成することはできません。


2
これは根本的な原因ではなく、「悲しい制限」でもありません。

なぜ制限ではないと思いますか?その能力があればいいのにと思います。また、プログラマーがコンテナーへのポインターを配置することを余儀なくされ、削除について心配する場合、プログラマーにはいくらかのオーバーヘッドがあります。同じコンテナに異なるサイズのオブジェクトがあると、パフォーマンスが低下することに同意します。
David Gruzman 2010年

仮想関数は、所有しているオブジェクトのタイプに基づいてディスパッチします。コンストラクターの全体的なポイントは、まだオブジェクトを持っていないということです。静的仮想関数を使用できない理由に関連しています。オブジェクトもありません。
MSalters 2010年

クラスコンテナのテンプレートはオブジェクトである必要はなく、クラスファクトリであり、コンストラクタはその自然な部分であると言えます。
David Gruzman 2010年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.