web-dev-qa-db-ja.com

ポリモーフィックC ++参照

howポインタではなく、参照を使用してポリモーフィズムを実行できるのではないかと思っていました。

明確にするために、次の最小限の例を参照してください。

class A;

class B {
  public:
    A& a; ///////////////// <- #1
    B();
    void doStuff();
};

class A {
  public:
    virtual void doSmth() = 0;
};
void B::doStuff() {
  a.doSmth();
}

class A1 : public A {
  public:
    void doSmth() {
    }
};

B::B() : a(
    *        ////////////// <- #2
      (new A1)  /////////// <- #3
     ) {
}

これはコンパイルされて機能しますが、ここで最も重要な点は、行#1aが参照であるため、多態的に使用できるようにするためです(これは実際の単語ですか?)。行#3に示されているように、逆参照して「ポインターを参照に変換」する必要があります。

これは少し奇妙に思えますが、(よりクリーンなの意味で)より良い方法があるかどうか疑問に思いました。私だけでしょうか?

理論的根拠

newがまったく必要ないのであれば素晴らしいのですが、(!)Bを宣言するとき、A1(!)のインスタンスを作成する方法がわかりません。 Aは前方宣言であるため、A1Bと同じコンパイル単位で実装されます。それでも、この場合、動的メモリ割り当てが本当に必要ですか?これをどのように行いますか?

少し二重の質問でごめんなさい。

編集

注:Bは巨大で(テンプレートクラスを作成できません)、プログラムが終了するとスコープから外れます--aは小さく、2つの大きなモジュールが通信しますお互いに、Bのインスタンスが存続している限り必要になります(1つだけです)。

編集2

ABはどちらも事実上シングルトンであるため、staticのコンパイルユニットにA1Bインスタンスを簡単に作成できることに気づきました。 var] _、動的メモリ割り当てを回避します(2つのBsがあったとしても、Aの同じインスタンスを簡単に使用できます)。公平を期すために、私はこれを回答として投稿しませんでしたが、この解決策を思い付くように促した回答を受け入れます。

21
bitmask

奇妙なことは何もありません。多型はポインタの両方で機能しますおよび参照:

_struct Base { };
struct Derived : Base;

void foo(Base &);

int main() {
  Derived x;
  foo(x);    // fine
}
_

これを別の問題、つまり動的オブジェクトへの参照の作成と混同しています。

_T * pt = new T;
T & rt = *pt;

T & x = *new T;  // same effect
_

動的オブジェクトを追跡するのは一般的に非常に悪いスタイルであることに注意してくださいのみそれを削除する唯一の方法は_delete &x;_経由であり、xをクリーンアップする必要があることを確認するのは非常に難しいためです。

デザインには2つの直接的な選択肢があります:1)aBのメンバーオブジェクトにするか、2)aを_shared_ptr<A>_または_unique_ptr<A>_にして、イニシャライザーをa(new A1)に変更します。それはすべて、実際にポリモーフィックな動作が必要かどうか、つまり、_A1_以外のBに異なる派生クラスを割り当てるaの他のコンストラクターがあるかどうかによって異なります。

36
Kerrek SB

それでも、この場合、動的メモリ割り当てが本当に必要ですか?

いいえ。最初にA1を定義してから、それをBの通常のメンバーにします。

ポリモーフィズムは、参照とポインターの両方で問題なく機能します。

3
Puppy

これは確かに少し奇妙です。 (参照ではなく)タイプA1のメンバー変数が必要な場合は、A1の定義がBの定義の前に表示されるように、コードを再配置してみませんか?

3

えーと、これでは不十分ですか?

#include <iostream>

struct A;

struct B
{
  B(A& a);

  void foo();

  A& _a;
};

struct A
{
  virtual void foo() =0;
};

struct A1 : public A
{
  virtual void foo() { std::cout << "A1::foo" << std::endl; }
};

B::B(A& a) : _a(a) {}
void B::foo() { _a.foo(); }


int main(void)
{ 
  A1 a;  // instance of A1
  B b(a); // construct B with it

  b.foo();
}
1
Nim

それでも、この場合、動的メモリ割り当てが本当に必要ですか?

動的メモリ割り当てまたは参照をBのctorに挿入します。

0
Simon

参照がポインターのように多形的に機能する理由を想像するのは簡単です(言うまでもなく、参照はポインターとして実装されることがよくあります)。簡単な例を次に示します。

class Base { 
public:
    virtual void something() { }
};

class Derived : public Base {
public:
    void something() { }
};

Base& foo() {
    static Derived d;
    return d;
}

foo().something(); // calls Derived's something

また、参照用に動的メモリを割り当てるのはなぜですか?この場合、おそらく参照を使用するべきではありません。また、参照メンバーを使用してクラスを作成すると、割り当てが効果的に防止されます(誰かが非常によく言うのを聞いたように)。

0
Seth Carnegie