4

わかりました、aを aに変換する問題と、それが禁止されている理由について扱っている FQA のこのエントリを読んでいましたが、問題は aではないものに代入できることであることがわかったので、それを禁止します。Derived**Base**Base*Derived*

ここまでは順調ですね。

しかし、その原則を深く適用するのであれば、なぜそのような例を禁止しないのでしょうか?

void nasty_function(Base *b)
{
  *b = Base(3); // Ouch!
}

int main(int argc, char **argv)
{
  Derived *d = new Derived;
  nasty_function(d); // Ooops, now *d points to a Base. What would happen now?
}

nasty_function私はそれがばかげたことをすることに同意します。興味深いデザインを可能にするので、その種の変換を許可することは問題ないと言えますが、二重間接化についても言えBase **ます。Base **のように、それがどこから来るのか本当にわからないからですBase *

では、問題は、この追加レベルの間接化の特別な点は何ですか? おそらく要点は、1 レベルの間接化だけで、それoperator=を回避するために virtual で遊ぶことができるということですが、同じ機構はプレーン ポインターでは利用できないのでしょうか?

4

6 に答える 6

16
nasty_function(d); // Ooops, now *d points to a Base. What would happen now?

いいえ、そうではありません。を指しDerivedます。この関数Baseは、既存のDerivedオブジェクトのサブオブジェクトを変更しただけです。検討:

#include <cassert>

struct Base {
    Base(int x) : x(x) {}
    int x;
};
struct Derived : Base {
     Derived(int x, int y) : Base(x), y(y) {}
     int y;
};

int main(int argc, char **argv)
{
  Derived d(1,2); // seriously, WTF is it with people and new?
                  // You don't need new to use pointers
                  // Stop it already
  assert(d.x == 1);
  assert(d.y == 2);
  nasty_function(&d);
  assert(d.x == 3);
  assert(d.y == 2);
}

d魔法のように にはなりませんBaseよね?そのままDerivedですが、Base一部変更しました。


写真で:)

オブジェクトは次のようBaseになりDerivedます。

レイアウト

2 つのレベルの間接参照がある場合、代入されるものがポインターであるため、機能しません。

ポインターの割り当て - 型の不一致

Base問題のとDerivedオブジェクトのどちらも変更しようとしていないことに注意してください。変更しようとしているのは中央のポインタだけです。

ただし、間接化のレベルが 1 つしかない場合、コードは、オブジェクトが許可する方法でオブジェクト自体を変更します (プライベートにする、非表示にする、または a から代入演算子を削除することによって禁止できますBase)。

間接レベルが 1 つだけの代入

ここでポインタが変更されていないことに注意してください。これは、オブジェクトの一部を変更する他の操作と同じd.y = 42;です。

于 2012-06-29T15:35:36.100 に答える
7

いいえ、nasty_function()思ったほど悪くはありません。ポインターbが何かを指しているので 値をそれBaseに割り当てることは完全に合法です。Base

注意してください: あなたの "Ooops" コメントは正しくありません:dまだ呼び出し前と同じものを指してDerivedいます! その一部だけBaseが (値によって!) 再割り当てされました。それによって全体の一貫性が失われる場合は、仮想Derived化して再設計する必要があります。Base::operator=()次に、nasty_function()実際にはDerived代入演算子が呼び出されます(定義されている場合)。

したがって、あなたの例は、ポインターからポインターへのケースとはあまり関係がないと思います。

于 2012-06-29T15:36:33.647 に答える
2

*b = Base(3)メンバー関数 (演算子を含む) としてBase::operator=(const Base&)実際に存在するcallsが継承されます。Derived

次に何が起こるか ( を呼び出す) は"slicing"Derived::operator=(const Base&)と呼ばれることもありますが、そうです (通常は) 悪いことです。これは、C++の "become-like" 演算子 ( ) が遍在していることの悲しい結果です。=

(「become-like」演算子は、Java、C#、Python などのほとんどの OO 言語には存在しないことに注意してください。=オブジェクト コンテキストでは、C++ のポインタ代入に似た参照代入を意味します。)


要約:

型エラーを引き起こす可能性があるため、キャストDerived**->は禁​​止されています。Base**Derived*Base

あなたが言及した問題は型エラーではありません。これは別のタイプのエラーです。オブジェクトのインターフェイスの誤用derivedです。これは、親クラスの「become-like」演算子を継承したという残念な事実に由来します。


(はい、私はオブジェクト コンテキストで op= を意図的に「become-like」と呼んでいます。なぜなら、「代入」はここで何が起こっているかを示すのに適切な名前ではないと感じているからです。)

于 2012-06-29T15:36:51.827 に答える
0
*b = Base(3); // Ouch!

ここで、のオブジェクトは*b実際にはでありB、のベースサブオブジェクトです*d。そのベースサブオブジェクトのみが変更され、派生オブジェクトの残りの部分は変更されずd、派生タイプの同じオブジェクトを指します。

ベースの変更を許可したくない場合もありますが、型システムが正しいかどうかという点では。ADerived Baseです。

これは、不正なポインタの場合には当てはまりません。ADerived*はに変換可能Base*ですが、同じタイプではありません。型システムに違反しています。

あなたが求めている変換を許可することはこれと同じです:

Derived* d;
Base b;
d = &b;
d->x;
于 2012-06-29T15:40:25.043 に答える
0

あなたが与えたコードは理にかなっています。実際、代入演算子は Derived に固有のデータを上書きすることはできませんが、ベースのみを上書きできます。仮想関数は引き続き Derived からのものであり、Base からのものではありません。

于 2012-06-29T15:38:46.033 に答える
0

私の質問の良い答えを読んで、オブジェクト指向の第一原理に由来し、サブオブジェクトや演算子のオーバーロードとは何の関係もない問題の要点を得たと思います。

重要なのは、Deriveda が必要なときはいつでも aを使用できますBaseが (置換の原則)、派生クラスのインスタンスのポインターを a に割り当てる可能性があるため、 a が必要なときに aを使用できないということです。Derived*Base*

このプロトタイプで関数を取得します。

void f(Base **b)

fで多くのことを行うことができ、bとりわけそれを逆参照します。

void f(Base **b)
{
  Base *pb = *b;
  ...
}

a に渡した場合、 faをとして使用しDerived**ていることを意味します。Derived*Base*OtherDerived*Base*Derived*

一方、次の関数を使用します。

void f(Base *b)

fdereferencesの場合、 aの代わりに abを使用しますが、これはまったく問題ありません (クラス階層の正しい実装を提供する場合)。DerivedBase

void f(Base *b)
{
  Base pb = *b; // *b is a Derived? No problem!
}

別の言い方をすれば、置換の原則 (基本クラスの代わりに派生クラスを使用する) は、ポインターではなくインスタンスで機能します。これは、ポインター概念」が「継承するクラスのインスタンスを指す」ためですを継承するクラスのセットには、 を継承するクラスのセットが厳密に含まれます。AABaseDerived

于 2012-06-29T16:48:48.150 に答える