12

長い間、私はポインターを認識しており、長寿命のオブジェクトを扱う場合を除き、C++ ではやや不必要であり、参照は RAII モデルにより適したよりクリーンな代替手段ですnewdeleteただし、C++ で動的ポリモーフィズムを使用するときにポインターを回避する方法を確認することはまだできません。

次のクラスがあるとします。

class A
{
public:
    virtual void a() const = 0;
};

class B : public A
{
    virtual void a() const
    {
        std::cout << "B";
    }
};

class C : public A
{
    virtual void a() const
    {
        std::cout << "C";
    }
};

void invoke(const A& obj)
{
    obj.a();
}

int main()
{
    B b;
    invoke(b); // Prints B
}

オブジェクトはinvoke参照として渡すことができ、関連するポインターはありません (少なくともプログラマーの観点からは)。ただし、上記の例は本質的に静的ポリモーフィズムです。

bの型を別のものに依存させたい場合は、ポインターを使用する必要があります。

int main()
{
    A* a;
    if (something)
        a = new B;
    else
        a = new C;

    invoke(*a); // Prints B
    delete a;
}

これは私には地獄のように醜く見えます。確かに、スマート ポインターを使用できます。

int main()
{
    std::unique_ptr<A> a;
    if (something)
        a.reset(new B);
    else
        a.reset(new C);
    invoke(*a); // Prints B
}

しかし、スマート ポインターはポインターの単なるラッパーです。

これを回避し、ポインターを使用せずにポリモーフィック クラスを利用する方法があるかどうかを知りたいです。

4

6 に答える 6

7

このためのポインタを避けることはできません。それらが気に入らない場合は、C++ はあなたに適した言語ではありません。なぜなら、ポリモーフィックなことをしたい場合は、最も些細な使用法を乗り越えるためにポインターを使用する必要があるからです。ヒープ上にオブジェクトを構築する、つまり、使用newすることは、スタック構築オブジェクトのスコープ付きの有効期間を回避する方法です。これは、条件分岐内で物事を作成し、それらを親スコープ内の変数に割り当てたい場合に行う必要があります。あなたの型はコンパイル時にすべて決定可能であるため、実際にはポリモーフィズムも必要ありません。これを回避する方法はありません。

もちろん、スマート ポインターを使用すると、ポインターのライフサイクルに関する問題を回避するのに非常に役立ちますが、最終的にどのような優れた抽象化を使用しても、どこかにポインターが存在します。

于 2012-08-31T10:38:23.177 に答える
4

最後の例を考えると、ポインターの使用を避けることができます (ただし、スマートポインターに大きな問題は見られません)。

struct AHolder {
  std::shared_ptr<A> a;
  operator A const&() const {
    return *a;
  }
  AHolder(bool something)
  : a(something?std::make_shared<B>():std::make_shared<C>()) {
  }
};

これにより、ホルダーを実際のオブジェクトであるかのように使用できます。

int main() {
  AHolder a(true); // or whatever argument you like
  invoke(a);
}
于 2012-08-31T10:27:34.357 に答える
1

どんな種類のポインターも絶対に避けたい理由がわかりません。std::unique_ptr/を使用してもまったく問題ありませんstd::shared_ptr(もちろん必要な場合)。スマート ポインターは、ポインターの「単なるラッパー」ではありません。さまざまなセマンティクスから選択でき、さまざまな用途があります。

于 2012-08-31T10:25:31.420 に答える
1

おそらくあなたが考えているユースケースは、一種のfactoryのユースケースです。これは、スマートポインターを使用して完全にうまく機能し、まったく機能しませんnew

std::unique_ptr<Message> parse_message(char const * buf, std::size_t len)
{
    if (buf[0] == 'A') { return make_unique<RequestMsg>(buf + 1, len - 1); }

    if (buf[0] == 'R') { return make_unique<AnswerMsg>(buf + 1, len - 1); }

    return nullptr;
}

使用法:

auto msgptr = parse_msg(buf, len);

make_unique残念ながら現在の標準ライブラリにはありませんが、最終的には修正される予定です。)

于 2012-08-31T10:35:01.830 に答える
1

これを回避し、ポインターを使用せずにポリモーフィック クラスを利用する方法があるかどうかを知りたいです。

いいえ、これは c++ が最初に設計された方法です。スライスを回避するには、ポインターまたは参照を使用する必要があります。

于 2012-08-31T10:35:18.133 に答える
0

オブジェクトを動的に作成し、それらを関数スコープから離れて永続化する必要がある場合は方法がありません。ここでできる最善のことは、オブジェクトの作成をファクトリ ポインターとスマート ポインター内に隠すことです。

また、関数スコープで静的オブジェクトを検討することもできますが、これは良い解決策ではありません。この方法では、限られた数のオブジェクトしか作成できません。

template <class T, int Instance> T& create()
{
   static T instance;
   return instance;
}

MyClass& obj1 = create<MyClass, 1>();
MyClass& obj2 = create<MyClass, 2>(); 
MyClass& obj3 = create<MyClass, 3>(); 
// etc

このソリューションにはいくつかの制限があり、ほとんど使用できませんでした。オブジェクトは最初の関数呼び出しで静的に作成され、プログラムが終了するまで存続することに注意してください。

于 2012-08-31T10:38:46.700 に答える