10

私はC++にかなり慣れていませんが、これが私が抱えている問題です。2つのクラスがClientありHostます。そして、すべてがロードされたら、2つのボタンを押すオプションがあります。ボタン1を押すClientとロードされ、ボタン2を押すHostとロードされます。

今では両方ともClientかなりHost大きなクラスであり、両方をメモリに入れたくありません。したがって、私のアイデアはBaseクラスを作成することでした。次に両方Clientを作成しHost、基本クラスを拡張する必要があります。その後、私がしなければならなかったのはこれだけでした。

Base connection;

//If button 1 is pressed:
connection = Client();

//If button 2 is pressed:
connection = Host();

まあ、これはほとんど真実ではないように聞こえました、そして私がそれを試したとき、私はエラーを受け取りませんでした。ここで問題が発生し、Baseと呼ばれる関数がAあり、と呼ばClientれる関数がありますB。したがって、関数Bはクラスに固有Clientです。

関数を呼び出そうとすると、次のBエラーが発生します'class Base' has no member named 'B'Clientクラスと話しているのか、Host代わりに話しているのかをC ++に知らせるにはどうすればよいBaseですか?私はまた、この問題に対するまったく新しいアプローチを受け入れています。多分それは私の思考過程の単なる誤りです。

前もって感謝します!

4

4 に答える 4

6

オブジェクトスライシングと呼ばれる状況に遭遇しました。これは、 C++などの値セマンティクスを持つ言語で一般的な問題です。

オブジェクトのスライスは、サブタイプの値をconnectionスーパータイプの新しい場所(変数)に割り当てるときに発生します。これにより、インスタンスの新しいコピーが導入されますが、サブタイプではなくスーパータイプであるため、インスタンス化する具体的なクラスに関する情報が失われます。

これを回避するには、複数のオプションがあります。

古典的なアプローチポインタを使用します:

Base * connection;
connection = new YourConcreteType();

次に、このオブジェクトを使用するには、アスタリスク演算子()を使用して参照を解除する必要があります。*

(*connection).function();
connection->function();    // syntactic sugar, semantically equivalent

忘れないでください:使用後にオブジェクトを削除する必要があります:

delete connection;

これを単純化するために、C++では参照スマートポインターという2つの概念が導入されています。前者には1回だけ割り当てるという制限がありますが、構文的に最も単純なものです。後者はポインタアプローチに似ていますが、削除を気にする必要がないため、メモリリークの状況に遭遇する可能性は低くなります。

std::shared_ptr<Base> connection;

connection = make_shared<YourConcreteType>(); // construction via 'make_shared'

// ... use as if it were just a pointer ...

connection->function();

// no delete!

のような他の「スマートポインタ」タイプもありますunique_ptr。これは、ポインタを渡すつもりがない場合(スコープ内にある場合)に使用できます。

これで、両方のクラスの関数を別々に実装できます。ポリモーフィズムを利用するには、実行時に一方のサブクラスまたはもう一方のサブクラスの関数が呼び出されます。どちらが作成されたかに応じて、基本クラスの関数を宣言する必要がありますvirtual。そうでない場合は、関数Base作成した具体的なタイプに関係なく、の定義が呼び出されます。

あなたの場合、タイプに応じて何か違うことをするはずの関数を呼び出したいと思います。あなたのアプローチは2つの異なる関数、つまりAとを導入することでしたが、基本クラスの純粋な仮想(=抽象)関数としてB単一の関数を宣言することができます。つまり、 「この関数はサブで実装されます。クラス」、および2つのサブクラスで独立して定義します。handleEvent

Base {
    ....
    virtual void handleEvent(...) = 0; // "= 0" means "pure"
};

// Don't provide an implementation

Client {
    void handleEvent(...); // override it
};

// define it for Client:
void Client::handleEvent(...) {
    ...
}

Host {
    void handleEvent(...); // override it
};

// define it for Host:
void Host::handleEvent(...) {
    ...
}
于 2013-01-23T23:46:26.300 に答える
3

これを行うとき:

Base connection;

connectionタイプに必要なメモリを正確に持つオブジェクトを作成しますBase

これを行うとき:

connection = Client();

割り当てられたメモリを拡張したり、インスタンスに変換connectionしたりすることはありません。Client代わりに、小さなオブジェクトに「スライス」される大きなオブジェクトを作成しています。

あなたがしたいのは、ポインタ(または参照またはスマートポインタ)を使用して、保持するのが1つのタイプまたは他のタイプである可能性のあるオブジェクトのアドレスだけになるようにすることです。このような:

Base *connection;
...
connection = new Client();

最初のステートメントは型付きオブジェクトへのポインタを作成しBase、2番目のステートメントは型付きオブジェクトにメモリをClient割り当て、それを初期化し、そのアドレスをに割り当てますconnection

于 2013-01-23T23:46:08.533 に答える
2

まだBaseを使用したい場合は、これらの関数を使用するためにこれを行う必要があります。

if (Button1) {
    dynamic_cast<Client*>(connection)->A();
} else {
    dynamic_cast<Host*>(connection)->B();   
}  

そして、接続をポインタにする必要があります。Base * connection

しかし、これは本当に理想的ではありません。他の回答と同じように、別の方法で調査する必要があります。

于 2013-01-23T23:47:55.450 に答える
2

まず、この行

connection = Client();

は、代入演算子を使用して、一時オブジェクトからの状態を設定してconnectionBaseますClientconnectionまだBaseオブジェクトです。あなたができることは次のとおりです。

std::unique_ptr<Base> makeBase(someFlagType flag)
{
  if (flag) {
    return std::unique_ptr<Base>(new Cient);
  } else {
    return std::unique_ptr<Base>(new Host);
  }
}

それで

std::unique_ptr<Base> b = makeBase(myFlag);
b->someBaseMethod();

キャスト部分に関しては、子タイプにキャストしなければならない場合は、クラスのデザインを考え直す必要があると思います。

于 2013-01-23T23:49:15.817 に答える