1

未定義の動作、メモリリーク、またはその他の正しいコードセットでのクラッシュを引き起こす可能性のあるC ++オブジェクトスライス効果の例はありますか?たとえば、クラスAB(から継承されたA)は正しくて健全であるが、明らかに呼び出すvoid f(A a)と厄介なことが発生します。

テスト問題を作成するために必要です。目標は、参加者がスライス現象を認識しているかどうかを知ることです。その正しさは意見の問題であってはならないサンプルコードスニペットを使用します。

4

3 に答える 3

4

が実際に「正しくて健全」である場合A、スライス(ベースサブオブジェクトのコピー)は明確に定義されており、言及した問題のいずれも発生しません。コピーがのように動作することを期待している場合、それが引き起こす唯一の問題は予期しない動作ですB

が正しくコピーできない場合A、スライスすると、そのタイプのオブジェクトのコピーで問題が発生します。たとえば、オブジェクトが保持するポインタを削除するデストラクタがあり、コピーすると同じものへの新しいポインタが作成される場合、両方のデストラクタが同じポインタを削除すると、未定義の動作が発生します。これは、スライス自体の問題ではありませんが、スライスされたオブジェクトの無効なコピーセマンティクスでは問題になります。

于 2012-11-28T13:35:19.453 に答える
1

オブジェクトのスライスが実際に問題になるのは、基本クラスへのポインターまたは参照を介して派生クラスを操作する場合のみです。その後、派生クラスの追加データは変更されませんが、基本部分の追加データは変更される可能性があります。これにより、派生クラスの不変条件が壊れる可能性があります。簡単な例については、http://en.wikipedia.org/wiki/Object_slicingを参照してください。

ただし、これは(派生クラスの)設計上の欠陥と考えます。基底クラスへのポインターまたは参照引数を取得したメソッドを含む、正当なメソッドでクラスにアクセスすると、その不変条件が壊れる可能性がある場合、そのクラスは正しく設計されていません。これを回避する 1 つの方法は、 base を宣言しprivateて、派生クラスがその base 経由で合法的にアクセスできないようにすることです。

例は次のとおりです。

class A
{
  int X;
  A(int x) : X(x) {}
  void doubleX() { X+=X; }
  /* ... */
};

class B : public A
{
  int X_square;
  B(int x) : A(x), X_square(x*x) {}
  /* ... */      
};

B b(3);
B.doubleX();   /// B.X = 6 but B.X_square=9

この例から、これが B の単純な設計上の欠陥であることも明らかです。この例では、

void B::doubleX() { A::doubleX(); X_squared=X*X; }

として、問題を解決しません。

A&a=b;
a.doubleX();

それでも不変条件を破ります。ここでの唯一の解決策は、ベースをAプライベートに宣言するか、より良い方法として、Aのベースではなくプライベート メンバーを作成することですB

于 2012-11-28T13:46:51.613 に答える
1

このような例はいつでも作成できます

struct A {
  A() : invariant(true) {}
  virtual void do_sth() { assert(invariant); }
protected:
  bool invariant;
};

struct B : A {
  B() { invariant=false; }
  virtual void do_sth() { }
};

void f(A a)
{
  a.do_sth();
}

もちろん、Aコピー コンストラクター/代入演算子が不変式が true かどうかをチェックしない場合、これは内部で防止できます。

不変式がブール値よりも暗黙的である場合、これらのことは非常にわかりにくい場合があります。

于 2012-11-28T13:33:33.933 に答える