1

私が抱えている問題は、ポリモーフィック クラスにあります。アニマル クラスには、「virtual animal* get() = 0;」という関数があります。これは、dog クラスで"dog* get() { speak2(); return this; }"として定義されています。

少し前に、戻り値の型をこのように変更することは合法であると読んだことがありますが、期待どおりに動作しないようです: get 関数が呼び出されると、期待される値が出力されますが、戻り値を代入しようとすると無効な変換エラーが発生し、 speak2()関数を呼び出そうとすると、そのようなメンバーがないと表示されます。

私が探しているのは、「barn.front()->get()->speak2();」の行に沿って何かを呼び出せるようにすることです。. dynamic_casts やそのようなキャストなしでこれに似たものを達成する方法はありますか?

読みやすいように論理的な方法でクラスに名前を付け、次のコード全体にコメントの形式でいくつかのメモを追加しました。

#include <iostream>
#include <vector>
using namespace std;

class animal
{
public:
    virtual ~animal() {}
    virtual void speak1() = 0;
    virtual animal* get() = 0;
};

class dog : public animal
{
public:
    void speak1() { cout << "print-speak1!"; }
    void speak2() { cout << "print-speak2!"; }
    dog* get() { speak2(); return this; }
};

int main()
{
    vector<animal*> barn;

    barn.push_back(new dog());

    barn.front()->speak1();                // prints "print-speak1!"
    barn.front()->get();                   // prints "print-speak2!"

    barn.front()->get()->speak2();
    // error: 'class animal' has no member named 'speak2'
    // but then why does "barn.front()->get();" print "print-speak2!"?

    dog* dogptr = barn.front()->get();
    // error: invalid conversion from 'animal*' to 'dog*' [-fpermissive]

    dogptr->speak2();
    // for the sake of -Werror=unused-variable

    for(vector<animal*>::iterator i = barn.begin(); i != barn.end(); ++i)
    {
        delete *i;
    }
    barn.clear();

    return 0;
}
4

4 に答える 4

1

インターフェイスへのプログラミングの要点は、インターフェイス クラスをアクセス ポイントとして使用し、実装された具象型に依存してインターフェイスに準拠することです。これは、動物へのポインターを介してベクター内の実際の動物にアクセスする必要があることを意味します。animal クラス内で speak2() を純粋仮想関数として定義し、dog* の代わりに animal* を使用すると、プログラムが実行されます。すべての動物に speak2() 機能があるかどうかは疑問ですが、それは設計上の問題です。ライブラリ コード内で名前空間ディレクティブを使用することは適切な方法ではないため、クラス実装の下に using ディレクティブを配置したことに注意してください。

変更を加えたコードは次のとおりです。

#include <iostream>
#include <vector>

class animal
{
public:
    virtual ~animal() {}
    virtual void speak1() = 0;
    virtual void speak2() = 0;
    virtual animal* get() = 0;
};

class dog : public animal
{
public:
    void speak1() { std::cout << "print-speak1!" << std::endl; }
    void speak2() { std::cout << "print-speak2!" << std::endl; }
    dog* get() { speak2(); return this; }
};

using namespace std;

int main()
{
    vector<animal*> barn;

    barn.push_back(new dog());

    barn.front()->speak1();                // prints "print-speak1!"
    barn.front()->get();                   // prints "print-speak2!"

    barn.front()->get()->speak2();
    // error: 'class animal' has no member named 'speak2'
    // but then why does "barn.front()->get();" print "print-speak2!"?

    animal* dogptr = barn.front()->get();
    // error: invalid conversion from 'animal*' to 'dog*' [-fpermissive]

    dogptr->speak2();
    // for the sake of -Werror=unused-variable

    for(vector<animal*>::iterator i = barn.begin(); i != barn.end(); ++i)
    {
        delete *i;
    }
    barn.clear();

    return 0;
}
于 2013-01-07T14:51:09.340 に答える
1

コードでは、異なる時点で 2 つの処理が行われます。まず、コンパイラは as の戻り値の型を認識barn.front()animal*ます。あなたが何をしても。speak2()型のメンバーの呼び出しanimal*は常に失敗します。

あなたのメンバーget()は仮想関数であり、 with linebarn.front()->get();は仮想ディスパッチを使用して呼び出されます。これは、呼び出される関数は、動物の実際の(いわゆる動的)タイプに基づいて、実行時にのみ認識されることを意味します。そうすれば、各動物の の動作を変更できます。get()

の戻り値の違いget()はここでは問題になりません。コンパイラが行う型チェックは (明らかに) コンパイル時に行われるため、animal::get(). 共変の戻り値の型は、 static 型のオブジェクトを直接呼び出す場合にのみ役立ちますdog

あなたの場合、ハックな方法は、型を にキャストするdogことです。

static_cast<dog*>(barn.front())->speak2();

もちろん、barn.front() の実際の型が犬でない場合はやけどします。つまり、未定義の動作を意味します-プログラムがクラッシュしたり、例外をスローしたり、破損したデータを静かに続行したりする可能性があります。

私の意見では、より正しいアプローチは、次のように、さまざまなアクションを汎用インターフェイスに分離することです。

class animal
{
public:
    virtual ~animal() {}
    virtual void makeSound() = 0;
};

class dog : public animal
{
public:
    void bark() { cout << "hoof"; }
    void makeSound() { bark(); }
};

class cat : public animal
{
public:
    void meow() { cout << "meow"; }
    void makeSound() { meow(); }
};

int main()
{
    vector<animal*> barn;
    barn.push_back(new dog());
    barn.push_back(new cat());
    barn.front()->makeSound();
    barn[1]->makeSound();
}
于 2013-01-07T15:15:56.660 に答える
0

与えられた

class animal
{
public:
    virtual animal * get () = 0;
};


class dog : animal
{
public:
    virtual dog * get () {...}
};

それで、

// Compiler sees call to "dog::get()".
dog * d = new dog();
d->get();


// Compiler sees call to "animal::get()", which may return
// an instance of any type derived from "animal".
animal * a = d;
d->get();
于 2013-01-07T15:32:04.827 に答える
0

を使用すると、ポインタbarn->front()->get()にアクセスできます。は に対して定義されていないため、.animal*Speak2()animalno member error

于 2013-01-07T14:47:46.197 に答える