8

一般に、C ++では、ポリモーフィックオブジェクトへのパラメーターを受け取る関数がそのオブジェクトへのポインターを受け取るコードが望ましいように見えます。

class Base {};
class DerivedA : public Base {};
class DerivedB : public Base {};

void OperateOnObject(Base* obj) {};

vector<Base*> objects;
objects.push_back(new DerivedA());
objects.push_back(new DerivedB());

for (size_t i=0; i<objects.size(); i++) {
  OperateOnObject(objects[i]);
}

なぜそれは通常、参照ではなくOperateOnObject()ポインタを取るように書かれているのですか?Baseスライスに潜在的な問題はありますか?(たとえば、vtableが失われます)

似たような質問に対するこの回答を見たことがありますが、同じ問題に対処しているようには見えません。

4

2 に答える 2

10

スライスに問題はありません---クラスには仮想関数がないためvtableは必要ありませんが、仮想関数があったとしても、参照渡しではオブジェクトがコピーされないため、何もスライスされません。

ポインタを介した場合とまったく同じように、参照を介して仮想呼び出しを行うことができます。私が知る限り、実用的な理由はありませんが、それはスタイルのためだけです。

おそらくそれは、ポリモーフィックオブジェクトが一般にポインタを使用して作成さnewれるという事実と関係があります。もちろん、参照のコンテナを持つことはできないため、ポインタ(またはスマートポインタ)によってコンテナに格納する必要があります。その場合、常にポインタでそれらを操作する方が一貫しているように見えるかもしれません。

また、OperateOnObjectのような標準アルゴリズムで使用する場合for_eachは、コンテナの要素タイプ(ポインタ)を受け入れるか、逆参照を行う関数アダプタでラップする必要があります。標準のC++にはそのアダプターがないため、スタイルのベースにするのは面倒な世界です。

OperateOnObject関連:参照を取得し、イテレータを使用してベクトルを反復するとどうなるかを見てください。

for (vector<Base*>::iterator i = objects.begin(); i != objects.end(); ++i) {
    OperateOnObject(**i);
}

二重間接参照があなたを悩ませたり混乱させたりする20回目は、の署名を変更しますOperateOnObject;-)

余談ですが、一部のスタイルガイドでは、型が多態的な基本クラスであるかどうかに関係なく、非定数参照を渡さないように警告しています。これは、参照渡しと値渡しの違いをすぐに見分けることができないためです。コールサイトでコードを読んでいます。だから彼らはOperateOnObjectとにかくポインタを取ることを好むでしょう。

個人的には、引数は少し弱いと思います-関数の名前はそれが何をするかを大まかに教えてくれるはずです、そして特にのようなステートメントChangeTheObject(my_object);はそれが戻り値を使用していないのでそれを変更しなければならないことを私にそれほど微妙に示唆していません口論。しかし、スタイルに正しく従えば、ミューテーターを純粋関数から明確かつ一貫して区別することにはいくつかの利点があることを私は受け入れます。

于 2012-05-11T10:54:22.990 に答える
9

参照はスライスに関する問題を提示しません-この点で、参照はポインタと同じくらい優れています。コードでセマンティクスを使用できるようにしたい場合を除いて、ポインターを好む「難しい」理由はありませんNULL(つまり、参照を使用する場合は存在しない「何も」を渡さない機能)。

ポインター型は、コレクションのセマンティクスに一致するポリモーフィック関数のパラメーターとしてよく使用されると思います。参照を作成できないため、vectorコードは、コレクションから取得したポインターを関数に渡す前に逆参照する必要があります。これは迷惑かもしれません。

于 2012-05-11T10:55:13.497 に答える