1

私は持っています:

class BASE{
public:
    virtual void func1() = 0;
};

次に、次のような派生クラスがあります。

class Derived1 : public BASE{
public:
    void func1() {...implementation....}
};

class Derived2 : public BASE{
    public:
        void func1() {...implementation....}
    };

主に私は(擬似コード)のようなことをしたいと思います:

if ( cond ){
   BASE b := new Derived1
}
else{
   BASE b := new Derived2
}
b.func1();

そのため、呼び出されるfunc1()関数は、派生クラスに特化した関数です。

私がやろうとした:

BASE b = new Derived1();

しかし、コンパイラは文句を言います。

C ++でそれを行うことは可能ですか?どのように?

4

7 に答える 7

3

ポインタ(または参照)を使用します。そうしないと、オブジェクトは通常のBASEインスタンスにコピーされます。

BASE* b = new Derived1;
b->func1();

// later...
delete b;

また、この場合、デストラクタを仮想化することを忘れないでください。

まったく新しい/削除することは、ポリモーフィズムを機能させるためだけに少し面倒なので、最近では人々はスマートポインタを使用する傾向があります。

std::shared_ptr<BASE> b=std::make_shared<Derived1>();
b->func1();
于 2012-05-06T12:30:23.407 に答える
3

どうやらあなたはJavaのようなガベージコレクションされたオブジェクト指向言語に慣れているようです。この質問にうまく答えるには、この観点からそれを行うのがおそらく良いでしょう。

あなたが何かを書くとき

Base b = new Derived1();

Javaでは、次のことが起こります。

  1. 汎用オブジェクトへのポインタ" &b"(これを呼びます)がスタックに割り当てられますβBase
  2. 新しいDerived1オブジェクト(これを呼びますd)がヒープに割り当てられます
  3. βオブジェクトを指すように設定されDerivedます。

Javaでこれを簡単に回避できる理由は、ガベージコレクターがあるためです。βこれは、スタック上にあり、を指している限り、何の効果もありません。これはd、GCdがまだアクセス可能であり、おそらく使用中であることを認識しているためです。ただし、ポインタが参照しなくなるとすぐにd(たとえば、宣言した関数bがスコープを離れたため)、GCは。によって占有されていたスペースを解放することが許可されdます。簡単ですが、ガベージコレクションにはいくつかの欠点があります。これはC++では望ましくありません。

したがって、C ++では、次のようなことを行うと

Base* b = new Derived1();

これはJavaバージョンに直接対応しているため、問題があります。bスコープを離れると、何も参照されdなくなりますが、それでもヒープ上にあります。自分で削除する必要があります

delete b;

(これには、削除された時点を正確に判断できるという大きな利点がありますが、ガベージコレクターは、かなり長い間無駄に放置し、メモリが不足し始めたときにのみ消去する可能性があることに注意してください)。しかし、繰り返しになりますが、これだけでは十分ではありません。ガベージコレクタとは異なり、プログラムはオブジェクトではなくオブジェクトをb指していることを自動的に認識しないため、削除は。を処理していると見なします。しかし、これを理解するのは簡単です。次のような仮想デストラクタをクラスに含めます。Derived1BasedeleteBaseBase

class Base{
 public:
  virtual void func1() = 0;
  virtual ~Base() {}  
};


さて、これを手作業ですべて削除する必要があるのは明らかに少し危険です。それを忘れると、メモリリークが発生します。つまり、プログラムが実行されている限り、オブジェクトがヒープから削除されることはありません。このため、プレーンなCスタイルのポインターの代わりにスマートポインターを使用する必要があります。これにより、スコープを離れるときに、それらが指すオブジェクトが自動的に削除されます。C ++ 11では、これらは標準ライブラリ(ヘッダー<memory>)で定義されています。あなたはただする

std::unique_ptr<Base> b(new Derived1());

そして今、bスコープを離れると、Derived1オブジェクトは自動的に削除されます。

これらすべての例で、仮想関数の呼び出しは同じように機能します。

b->func1();
于 2012-05-06T13:23:51.393 に答える
2

これを行うには2つの方法があります。

  • 基本クラス型のポインタを使用します。

    Base *b = new Derived1;

  • コピー構造から派生したものを作成する場合は、基本クラス型のconst参照を使用します。

    Base const& b = Derived;

  • 他の場所で作成された派生クラスオブジェクトを割り当てる場合は、基本クラスタイプの非定数参照を使用します。

    Derived d; Base& b = d;

ニット:

  • 継承するには、クラス定義でこれを指定する必要があります。

    class Derived1 : public Base {

  • 関数には戻り値が必要です。

    class B{ public: virtual void func() = 0; };

于 2012-05-06T12:31:52.623 に答える
1

通常のオブジェクトにポインタを割り当てようとしています。

new Derived1()

この式はポインタを返します。BASEポインタ( )を作成するか、スタック上にポインタを作成する必要がありますBASE *(できれば、Base b;)。ただし、この場合、クラス間で変換するにはポインタ(または参照)が必要です。

また、if-elseステートメント内にオブジェクトを作成することはできません。これは、後で使用するときにスコープ外になるためです。

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

BASE *b;

if (cond)
    b = new Derived1();
else
    b = new Derived2();

b->func1();

もちろんdelete、後でポインタを覚えておく必要があります。代わりに、単にスマートポインタを使用することを検討してください。

于 2012-05-06T12:27:59.417 に答える
1

ポインタ(およびそのトラップ)の使用については説明しました。動的メモリ割り当てを使用せずにポリモーフィズムを使用する方法を紹介します。おそらく、私がそれについて考えることすら異端者であることを証明します。

まず、コンパイルされるようにパッチを適用した元のコードを見てみましょう。

void foo(bool const cond) {
    Base* b = 0;
    if (cond) { b = new Derived1(); }
    else      { b = new Derived2(); }

    b->func1();
    delete b; // do not forget to release acquired memory
}

virtualのデストラクタがあると仮定すると、これは正常に機能しますBase

プログラマーの進化における次の論理的なステップは、スマートポインターを使用して、deletedelete初心者および専門家のライブラリ作成者のみが使用する)書き込みを回避することです。

void foo(bool const cond) {
    std::unique_ptr<Base> b;
    if (cond) { b.reset(new Derived1()); }
    else      { b.reset(new Derived2()); }

    b->func1();
}

もちろん、それでもvirtualデストラクタが必要です。Base

この関数が2つのことを行うことを理解しましょう。

  • 条件を与えられたクラスを選択してください
  • 生成されたインスタンスでいくつかの作業を行います

たとえば、ビルド作業を抽出することで、それを分解できます。

std::unique_ptr<Base> build(bool const cond) {
    if (cond) { return { new Derived1() }; }
    return { new Derived2() };
}

void foo(bool const cond) {
    std::unique_ptr<Base> b = build(cond);
    b->func1();
}

これはほとんどの人がしていることです。

ビルドを分離する代わりに、実際の作業を分離できる別の可能性があると私は主張します。

void dowork(Base& b) { b.func1(); /* and perhaps other things */ }

void foo(bool const cond) {
    std::unique_ptr<Base> b(cond ? new Derived1() : new Derived2());
    work(*b);
}

実際にさらに一歩進めることができます。

void foo(bool const cond) {
    if (cond) {
        Derived1 d;
        work(d);
    } else {
        Derived2 d;
        work(d);
    }
}

ポリモーフィズムは動的メモリ割り当てを必要としません。

そして、あなたはこの最後の例の何が楽しいか知っています:

  • C++11の移動セマンティクスがなくても完全に正常に機能します
  • virtualデストラクタが必要ありませんBase
于 2012-05-06T13:54:10.820 に答える
0

これを忘れましたか?

class Derived1 : public BASE
{
};

少なくとも私はあなたのコードにはそれを見ていません-派生しているBASEクラス

于 2012-05-06T12:29:39.070 に答える
0

関数のシグネチャが同じである場合、この状況ではおそらくインターフェイス宣言の方が適切です...次に、各クラスにインターフェイスを実装させ、宣言したインターフェイスタイプの変数を宣言します...

于 2012-05-06T12:31:10.707 に答える