3

baseインターフェイスに純粋仮想メソッドがあり、次のリストが返されるとしsomethingます。

class base 
{ 
public:
     virtual std::list<something> get() = 0; 
}; 

クラスを継承する2つのクラスがあるとしますbase

class A : public base 
{ 
public:
     std::list<something> get();
}; 

class B : public base 
{ 
public:
     std::list<something> get(); 
};

Aクラスだけがを返すことができるようにしたいのですが、たとえば次のようにlist<something>、ポインタを使用してリストを取得する可能性も必要です。base

base* base_ptr = new A();
base_ptr->get();

私がしなければならないこと?

このリストへのポインタを返す必要がありますか?参照?

クラスのメソッドからnullポインタを返す必要がありますBか?Bまたは、オブジェクトを使用してリストを取得しようとしたときに例外をスローする必要がありますか?baseまたは、クラスメソッドを変更して、get純粋ではなく、baseクラスでこれを機能させる必要がありますか?

私は何か他のことをする必要がありますか?

4

3 に答える 3

9

他に何もすることはありません。あなたが提供するコードはまさにそれを行います。

基本クラスへのポインタを取得すると、メソッドは基本クラスで宣言されており、仮想であるため、実際の実装はクラス仮想関数テーブルで検索され、適切に呼び出されます。

それで

base* base_ptr = new A();
base_ptr->get();

A :: get()を呼び出します。実装からnullを返さないでください(nullはstd :: list <something>に変換できないため、返すことはできません)。基本クラスのメソッドは純粋な仮想として宣言されているため、A/Bで実装を提供する必要があります。

編集

Bも基本クラスを継承し、基本クラスには派生クラスでオーバーライドする必要がある純粋仮想メソッドがあるため、Aだけがstd :: list <something>を返し、Bは返さないようにすることはできません。基本クラスからの継承は「is-a」関係です。私が見ることができた他の唯一の方法は、クラスからプライベートに継承することですが、それは派生からベースへの変換を妨げるでしょう。

Bにgetメソッドを持たせたくない場合は、baseから継承しないでください。いくつかの選択肢は次のとおりです。

B :: get()で例外をスローする:B :: get() で例外をスローすることもできますが、直感に反するため、論理的根拠を十分に説明してください。私見これはかなり悪い設計であり、基本クラスを使用している人々を混乱させるリスクがあります。これはリークの多い抽象化であり、避けるのが最善です。

個別のインターフェース: ベースを個別のインターフェースに分割することができます:

class IGetSomething
{
public:
    virtual ~IGetSomething() {}
    virtual std::list<something> Get() = 0;
};

class base
{
public:
    // ...
};

class A : public base, public IGetSomething
{
public:
    virtual std::list<something> Get()
    {
        // Implementation
        return std::list<something>();
    }
};

class B : public base
{

};

IGetSomethingは純粋なインターフェイスであるため、この場合の多重継承は問題ありません(メンバー変数や非純粋なメソッドはありません)。

EDIT2

コメントに基づくと、2つのクラス間で共通のインターフェースを持ちながら、一方の実装が実行する操作を実行できるようにしたいようですが、もう一方は提供しません。これはかなり複雑なシナリオですが、COMからインスピレーションを得ることができます(まだ私を撃たないでください):

class base
{
public:
    virtual ~base() {}
    // ... common interface

    // TODO: give me a better name
    virtual IGetSomething *GetSomething() = 0;
};

class A : public Base
{
public:
    virtual IGetSomething *GetSomething()
    {
        return NULL;
    }
};

class B : public Base, public IGetSomething
{
public:
    virtual IGetSomething *GetSomething()
    {
        // Derived-to-base conversion OK
        return this;
    }
};

今あなたができることはこれです:

base* base_ptr = new A();
IGetSomething *getSmthing = base_ptr->GetSomething();
if (getSmthing != NULL)
{
    std::list<something> listOfSmthing = getSmthing->Get();
}

複雑ですが、この方法にはいくつかの利点があります。

  • 具体的な実装クラスではなく、パブリックインターフェイスを返します。

  • あなたはそれが設計されているもののために継承を使用します。

  • 誤って使用することは困難です。baseはstd::list get()を提供しません。これは、具体的な実装間で一般的な操作ではないためです。

  • GetSomething()のセマンティクスについて明示的です。これにより、何かのリストを取得するために使用できるインターフェースを返すことができます。

    空のstd::listを返すだけではどうでしょうか?

それは可能ですが、悪いデザインです。ペプシにサービスを提供しないことを除けば、コーラとペプシを提供できる自動販売機を持っているようなものです。誤解を招く可能性があり、避けるのが最善です。

ブースト::optional<std :: list <something >>を返すだけではどうでしょうか?(アンドリューによって提案されたように)

これは、場合によってはNULLになることもあれば、そうでないこともあるインターフェースを返すよりも優れた解決策だと思います。そうすれば、それがオプションであり、間違いがないことを明示的に知っているからです。

欠点は、インターフェイス内にブーストを配置することです。これは避けたいと思います(ブーストを使用するのは私次第ですが、インターフェイスのクライアントにブーストの使用を強制する必要はありません)。

于 2012-07-06T08:13:19.947 に答える
1

戻らboost::optionalない能力が必要な場合に戻る(Bクラス)

class base 
{ 
public:
     virtual boost::optional<std::list<something> > get() = 0; 
}; 
于 2012-07-06T08:13:27.757 に答える
1

あなたがしていることは間違っています。両方の派生クラスに共通でない場合は、おそらく基本クラスに含めるべきではありません。

それはさておき、あなたが望むものを達成する方法はありません。Bにもメソッドを実装する必要があります。これはまさに純粋仮想関数の意味です。ただし、空のリストを返す、または1つの要素に事前定義された無効な値を含むリストを返すなど、特別な失敗ケースを追加することができます。

于 2012-07-06T08:21:49.427 に答える