オブジェクトのスライスとは何ですか?


回答:


609

「スライス」とは、派生クラスのオブジェクトを基本クラスのインスタンスに割り当てて、情報の一部を失うことです。その一部は「スライス」されます。

例えば、

class A {
   int foo;
};

class B : public A {
   int bar;
};

したがって、型のオブジェクトにBは2つのデータメンバーとがfooありbarます。

次に、これを書くとしたら:

B b;

A a = b;

次に、bメンバーに関する情報barがで失われaます。


66
非常に有益ですが、メソッド呼び出し中にスライスがどのように発生するかの例については、stackoverflow.com / questions / 274626#274636を参照してください(プレーンな割り当ての例よりも少し危険が強調されます)。
ブレアコンラッド

55
面白い。私はC ++で15年間プログラミングしてきましたが、効率と個人的なスタイルの問題として常にオブジェクトを参照渡ししてきたため、この問題は発生しませんでした。良い習慣がいかに役立つかを示しに行きます。
カールビーレフェルト

10
@Felixありがとうございますが、(ポインタ演算ではないため)キャストバックは機能しないと思います。これは、のコピーを持つA a = b; aタイプのオブジェクトAですB::foo。今キャストバックするのは間違いだと思います。

37
これは「スライス」ではなく、少なくともそれの無害な変形です。あなたがそうした場合、本当の問題が発生しますB b1; B b2; A& b2_ref = b2; b2 = b1。にコピーb1したと思われるかもしれませんb2が、そうではありません!あなたはコピーした部分b1b2(の一部b1それBから継承するA)、および他の部分の左b2変わらないが。b2は、数ビットのb1後にいくつかのチャンクが続くフランケンシュタインの生き物ですb2。うわっ!答えは非常に誤解を招くと思うので、反対投票。
fgp

24
@fgpコメントはB b1; B b2; A& b2_ref = b2; b2_ref = b1実際の問題が発生するのは、」であるはずです...非仮想代入演算子を持つクラスから派生した場合。されてAも、導出するためのもの?仮想機能はありません。タイプから派生する場合、そのメンバー関数を呼び出すことができるという事実に対処する必要があります。
curiousguy 2013年

510

ここでのほとんどの回答は、スライスの実際の問題が何であるかを説明できません。彼らはスライスの良性のケースのみを説明し、危険なケースは説明しません。あなたは二つのクラスを扱っていることを、他の回答のように、想定AしてB、どこBから(公的)を導出しますA

このような状況では、C ++を使用すると、インスタンス渡すことができますBAの代入演算子(ともコピーコンストラクタへの)。なぜなら、インスタンスのこの作品BAに変換することができますconst A&。これは、代入演算子とコピーコンストラクターが引数を期待しているものです。

良性のケース

B b;
A a = b;

そこでは何も悪いことが起こりません-あなたAはのコピーであるインスタンスを要求しましたB、そしてそれはまさにあなたが得るものです。確かに、a一部bののメンバーは含まれませんが、どのようにすべきですか?それはだA、すべての後に、ではないBので、それもしていない聞きましたこれらのメンバーについて、それらを格納することができるでしょうおろか。

危険な事件

B b1;
B b2;
A& a_ref = b2;
a_ref = b1;
//b2 now contains a mixture of b1 and b2!

それb2b1後でコピーになると思うかもしれません。しかし、悲しいかな、そうではありません!調べてみると、それb2はフランケンシュタインの生き物であり、b1Bから継承するチャンクA)のいくつかのチャンクと、b2(から継承するチャンク)のいくつかのチャンクでできていることがわかりますB含むます。痛い!

どうした?まあ、デフォルトではC ++は代入演算子をとして扱いませんvirtual。したがって、行a_ref = b1はの代入演算子でAはなくの代入演算子を呼び出しますB。なぜなら、非仮想関数のため、これは宣言:(正式に静的タイプ(ある)A&とは対照的に、呼び出される関数決定する)実際:(正式に動的(あろう型)B、以降a_refの参照のインスタンスB) 。現在、Aの代入演算子はで宣言されたメンバーのみを認識しているAため、それらのみがコピーされ、追加されたメンバーはB変更されません。

解決策

オブジェクトの一部にのみ割り当てることは、通常ほとんど意味がありませんが、残念ながら、C ++には、これを禁止する組み込みの方法がありません。ただし、自分でロールすることはできます。最初のステップは、代入演算子をvirtualにすることです。これにより、宣言された型ではなく、常に実際の型の代入演算子が呼び出されることが保証されます。2番目のステップでは、を使用して、割り当てられたオブジェクトに互換性のあるタイプがあることを確認します。第3のステップは(保護された!)メンバーの実際の割り当てを行うことですから、のは、おそらく使用したいと思うでしょう'S コピーするには、のメンバー。dynamic_castassign()Bassign()Aassign()A

class A {
public:
  virtual A& operator= (const A& a) {
    assign(a);
    return *this;
  }

protected:
  void assign(const A& a) {
    // copy members of A from a to this
  }
};

class B : public A {
public:
  virtual B& operator= (const A& a) {
    if (const B* b = dynamic_cast<const B*>(&a))
      assign(*b);
    else
      throw bad_assignment();
    return *this;
  }

protected:
  void assign(const B& b) {
    A::assign(b); // Let A's assign() copy members of A from b to this
    // copy members of B from b to this
  }
};

純粋に便宜上、Bのインスタンスを返すoperator=ことがわかっているため、は共変的に戻り値の型をオーバーライドすることに注意してくださいB


12
私見、問題は継承によって暗示される可能性のある2つの異なる種類の代替可能性があることです:derived値を期待するコードに任意の値を与えるかbase、または任意の派生参照をベース参照として使用できます。両方の概念に個別に対応する型システムを持つ言語が欲しいです。派生参照をベース参照の代わりに使用できる場合が多くありますが、派生インスタンスはベース参照の代わりに使用できません。インスタンスは変換可能でなければならないが、参照で代用すべきでない場合も多くあります。
スーパーキャット2013

16
あなたの「危険な」事件で何がそんなに悪いのか分かりません。1)クラスAのオブジェクトへの参照を取得し、2)オブジェクトb1をクラスAにキャストして、その内容をクラスAの参照にコピーします。ここで実際に間違っているのは、背後にある適切なロジックです指定されたコード。つまり、小さな画像フレーム(A)を取り、それを大きな画像(B)の上に配置し、そのフレームを塗りつぶして、後で大きな画像が見苦しく見えると不満を言う:)しかし、そのフレームの領域だけを考えると、画家が望んだように、それはかなりよさそうですよね?:)
Mladen B.

12
別の言い方をすれば、C ++はデフォルトで非常に強力な代替可能性を想定しているという問題です。つまり、サブクラスインスタンスで基本クラスの操作が正しく機能する必要があります。そして、コンパイラーが割り当てのように自動生成した操作についてもそうです。したがって、この点で独自の操作を台無しにしないだけでは十分ではなく、コンパイラーによって生成された間違った操作を明示的に無効にする必要もあります。または、もちろん、通常は良い提案となる公共の継承から離れてください;-)
fgp

14
もう1つの一般的なアプローチは、単純にコピーおよび代入演算子を無効にすることです。継承階層内のクラスの場合、通常、参照やポインターの代わりに値を使用する理由はありません。
Siyuan Ren 2014

13
なに?オペレーターに仮想のマークを
付ける

154

基本クラスAと派生クラスがある場合B、次のことができます。

void wantAnA(A myA)
{
   // work with myA
}

B derived;
// work with the object "derived"
wantAnA(derived);

ここで、メソッドにwantAnAはのコピーが必要ですderived。ただし、derivedクラスBがその基本クラスにない追加のメンバー変数を作成する可能性があるため、オブジェクトを完全にコピーすることはできませんA

したがって、を呼び出すために、wantAnAコンパイラーは派生クラスのすべての追加メンバーを「スライス」します。結果は、作成したくないオブジェクトになる可能性があります。

  • 不完全かもしれません
  • A-objectのように動作します(クラスのすべての特別な動作Bは失われます)。

41
C ++はJavaではありません!場合wantAnA(その名前が示すように!)望んでいるA、それはそれを取得するものです。そして、のインスタンスAは、ええと、のように動作しAます。それはどのように意外ですか?
fgp 2013年

83
@fgp:Aを関数に渡さないので、それは驚くべきことです。
ブラック

10
@fgp:動作は似ています。ただし、平均的なC ++プログラマにとっては、それほど明白ではない場合があります。私が質問を理解している限り、「不満」を言う人はいません。コンパイラーが状況を処理する方法についてだけです。Imho、(const)参照を渡すことでスライスを回避することをお勧めします。
ブラック

9
@ThomasWいいえ、継承は破棄しませんが、参照を使用します。wantAnAの署名がvoid wantAnA(const A&myA)である場合、スライスは行われていません。代わりに、呼び出し元のオブジェクトへの読み取り専用の参照が渡されます。
ブラック

14
問題は主に、コンパイラーがからderived型に実行する自動キャストにありますA。暗黙的なキャストは常に、C ++で予期しない動作の原因となります。これは、キャストが行われたことをコードをローカルで見るだけでは理解しにくいことが多いためです。
pqnet 2014

41

これらはすべて良い答えです。オブジェクトを値渡しまたは参照渡しする場合の実行例を追加したいと思います。

#include <iostream>

using namespace std;

// Base class
class A {
public:
    A() {}
    A(const A& a) {
        cout << "'A' copy constructor" << endl;
    }
    virtual void run() const { cout << "I am an 'A'" << endl; }
};

// Derived class
class B: public A {
public:
    B():A() {}
    B(const B& a):A(a) {
        cout << "'B' copy constructor" << endl;
    }
    virtual void run() const { cout << "I am a 'B'" << endl; }
};

void g(const A & a) {
    a.run();
}

void h(const A a) {
    a.run();
}

int main() {
    cout << "Call by reference" << endl;
    g(B());
    cout << endl << "Call by copy" << endl;
    h(B());
}

出力は次のとおりです。

Call by reference
I am a 'B'

Call by copy
'A' copy constructor
I am an 'A'

こんにちは。すばらしい答えですが、1つ質問があります。私がこのようなことをした場合** dev d; base * b =&d; **スライスも行われますか?
エイドリアン

@Adrian派生クラスにいくつかの新しいメンバー関数またはメンバー変数を導入すると、基本クラスポインターから直接アクセスできなくなります。ただし、オーバーロードされた基本クラス仮想関数の内部からは引き続きアクセスできます。これを参照してください:godbolt.org/z/LABx33
Vishal Sharma

30

「C ++スライス」のためのGoogleでの第三試合は私にこのWikipediaの記事与えhttp://en.wikipedia.org/wiki/Object_slicingを、これは(加熱しますが、最初のいくつかの記事では、問題を定義する):http://bytes.com/ forum / thread163565.html

つまり、サブクラスのオブジェクトをスーパークラスに割り当てるときです。スーパークラスは、サブクラスの追加情報を何も認識しておらず、それを格納する領域がないため、追加情報は「スライス」されます。

これらのリンクで「適切な回答」を得るのに十分な情報が得られない場合は、質問を編集して、探している情報をさらにお知らせください。


29

スライスの問題はメモリ破損を引き起こす可能性があるため深刻であり、プログラムがその影響を受けないことを保証することは非常に困難です。言語からそれを設計するには、継承をサポートするクラスは参照によってのみ(値ではなく)アクセスできる必要があります。Dプログラミング言語にはこの特性があります。

クラスA、およびAから派生したクラスBについて考えてみます。A部分にポインターpと、pがBの追加データを指すBインスタンスがある場合、メモリー破損が発生する可能性があります。次に、追加データが切り捨てられると、pはガベージを指しています。


3
メモリの破損がどのように発生するかを説明してください。
08年

4
私は間違い、コピーctorがvptrをリセットすることを忘れていました。ただし、Aにポインターがあり、BがそれをBのセクションを指すように設定すると、破損する可能性があります。
ウォルターブライト

18
この問題は、スライスに限定されません。ポインターを含むすべてのクラスは、デフォルトの代入演算子とコピーコンストラクターで不審な動作をします。
ウィーブル2009

2
@Weeble-これらの場合にデフォルトのデストラクタ、代入演算子、およびコピーコンストラクタをオーバーライドするのはこのためです。
Bjarke Freund-Hansen

7
@Weeble:オブジェクトのスライスが一般的なポインターのフィックスアップよりも悪いのは、スライスが起こらないようにするために、基本クラスがすべての派生クラスに変換コンストラクター提供する必要があるということです。(なぜですか?Derived暗黙的にに変換可能であるため、見落とされた派生クラスは、基本クラスのコピーctorによってピックアップされる可能性がありBaseます。)これは明らかに開閉原理に逆らい、メンテナンスの負担が大きくなります。
j_random_hacker 2012年

11

C ++では、派生クラスオブジェクトを基本クラスオブジェクトに割り当てることができますが、他の方法は不可能です。

class Base { int x, y; };

class Derived : public Base { int z, w; };

int main() 
{
    Derived d;
    Base b = d; // Object Slicing,  z and w of d are sliced off
}

オブジェクトのスライスは、派生クラスオブジェクトが基本クラスオブジェクトに割り当てられるときに発生します。派生クラスオブジェクトの追加の属性は、基本クラスオブジェクトを形成するために切り取られます。


8

C ++でのスライスの問題は、そのオブジェクトの値のセマンティクスから発生しますが、これは主にCの構造体との互換性が原因です。オブジェクトを実行する他のほとんどの言語で見られる「通常の」オブジェクト動作を実現するには、明示的な参照またはポインタ構文を使用する必要があります。つまり、オブジェクトは常に参照によって渡されます。

簡単に言えば、派生オブジェクトを基本オブジェクトに値で割り当てることによってオブジェクトをスライスするということです。つまり、残りのオブジェクトは派生オブジェクトの一部にすぎません。値のセマンティクスを維持するために、スライスは妥当な動作であり、他のほとんどの言語には存在しない比較的まれな用途があります。一部の人々はそれをC ++の機能であると考えていますが、多くの人はそれをC ++の奇妙な機能/誤機能の1つであると考えています。


5
通常のオブジェクトの動作」は「通常のオブジェクトの動作」ではなく、参照セマンティックです。それが関係しない方法で Cでstruct、互換性、またはその他の非感が任意のランダムなOOPの司祭はあなたに言いました。
curiousguy

4
@curiousguyアーメン、兄弟。値のセマンティクスがC ++を非常に強力なものにしているものの1つである場合に、C ++がJavaでないことからどれほど頻繁に打ちのめされるかを見るのは悲しいことです。
fgp 2013年

これは機能ではなく、奇妙な機能でもありません。argで関数を呼び出すか、(同じ)タイプのスタック変数を割り当てると、メモリ内でBase正確にsizeof(Base)バイトを取得する必要があるため、これは通常のスタックコピー動作です。)派生クラスのメンバーはコピーされません。オフセットはsizeofの外にあります。「データの損失」を回避するには、他の人と同じようにポインタを使用します。ポインタメモリは所定の位置とサイズに固定されていますが、スタックは非常に不安定です
Croll

間違いなくC ++の誤った機能。派生オブジェクトを基本オブジェクトに割り当てることは禁止する必要がありますが、派生オブジェクトを参照または基本クラスのポインターにバインドすることは問題ありません。
John Z. Li、

7

それで...なぜ派生した情報を失うことが悪いのですか?...派生クラスの作成者が表現を変更して、余分な情報を切り取ってオブジェクトが表す値を変更した可能性があるためです。これは、派生クラスを使用して特定の操作でより効率的な表現をキャッシュする場合に発生する可能性がありますが、基本表現に変換し直すにはコストがかかります。

また、誰かがスライスを避けるために何をすべきかについても言及する必要があると考えました... C ++コーディング標準、101のルールガイドライン、およびベストプラクティスのコピーを入手してください。スライスの扱いは#54です。

これは、問題を完全に処理するために多少洗練されたパターンを提案します:保護されたコピーコンストラクター、保護された純粋な仮想DoClone、および(さらに)派生クラスがDoCloneを正しく実装できなかったかどうかを通知するアサート付きのパブリックCloneがあります。(Cloneメソッドは、ポリモーフィックオブジェクトの適切な深いコピーを作成します。)

必要に応じて明示的にスライスできるように、ベースコンストラクタでコピーコンストラクタをマークすることもできます。


3
ベースエクスプリシットでコピーコンストラクタをマークすることもできます」これはまったく役に立ちませ
curiousguy 2012

6

1.スライスの問題の定義

Dが基本クラスBの派生クラスである場合、Derived型のオブジェクトをBase型の変数(またはパラメーター)に割り当てることができます。

class Pet
{
 public:
    string name;
};
class Dog : public Pet
{
public:
    string breed;
};

int main()
{   
    Dog dog;
    Pet pet;

    dog.name = "Tommy";
    dog.breed = "Kangal Dog";
    pet = dog;
    cout << pet.breed; //ERROR

上記の割り当ては許可されていますが、変数petに割り当てられた値はブリードフィールドを失います。これはスライス問題と呼ばれます

2.スライスの問題を修正する方法

この問題を解決するには、動的変数へのポインターを使用します。

Pet *ptrP;
Dog *ptrD;
ptrD = new Dog;         
ptrD->name = "Tommy";
ptrD->breed = "Kangal Dog";
ptrP = ptrD;
cout << ((Dog *)ptrP)->breed; 

この場合、ptrD(子孫クラスオブジェクト)によってポイントされている動的変数のデータメンバーまたはメンバー関数は失われません。また、関数を使用する必要がある場合、関数は仮想関数でなければなりません。


7
「スライス」の部分はわかりますが、「問題」はわかりません。dogクラスの一部ではない状態Petbreedデータメンバー)が変数にコピーされないという問題はどうしてpetですか?コードはPetデータメンバーのみに関係しています-どうやら。スライスは不要な場合は間違いなく「問題」ですが、ここではわかりません。
curiousguy

4
" ((Dog *)ptrP)"使用をお勧めしますstatic_cast<Dog*>(ptrP)
curiousguy

「ptrP」を介して削除するときに、文字列「breed」が仮想デストラクタなしで最終的にメモリリークすることを指摘することをお勧めします(「string」のデストラクタは呼び出されません)...何が問題なのですか?修正は主に適切なクラス設計です。この場合の問題は、継承時の可視性を制御するためのコンストラクターを作成するのが面倒で、簡単に忘れられることです。ポリモーフィズムは含まれておらず、言及もされていないため、コードを使用して危険ゾーンの近くに到達することはありません(スライスするとオブジェクトが切り捨てられますが、ここではプログラムがクラッシュしません)。
デュード

24
-1これは実際の問題を完全に説明することに失敗します。C ++にはJavaのような参照セマンティクスではなく、値セマンティクスがあるため、これはすべて予期されることです。そして、「修正」は本当に恐ろしい C ++コードの例です。動的割り当てに頼ることによるこの種のスライスのような存在しない問題の「修正」は、バグのあるコード、リークされたメモリ、および恐ろしいパフォーマンスのレシピです。スライスがうまくいかない場合もありますが、この回答では指摘できないことに注意してください。ヒント:参照を通じて割り当てた場合、問題が発生します
fgp 2013年

定義されていないタイプ(Dog::breed)のメンバーにアクセスしようとしても、SLICINGに関連するエラーにはならないことを理解していますか?
クロール

4

自分のクラスとプログラムが十分に構築/設計されていない場合を除いて、スライスはそれほど問題ではないように思えます。

スーパークラスタイプのパラメーターを取るメソッドにパラメーターとしてサブクラスオブジェクトを渡す場合、それを認識し、内部的に知っておく必要があります。呼び出されたメソッドは、スーパークラス(別名ベースクラス)オブジェクトのみで機能します。

ベースクラスがリクエストされた場所にサブクラスを提供すると、どういうわけかサブクラス固有の結果が生じ、スライスが問題になるという不合理な期待だけに思えます。メソッドの使用に関する設計が不十分であるか、サブクラスの実装が不十分です。私は通常、適切なOOP設計を犠牲にして便宜またはパフォーマンスの向上を優先した結果だと思います。


3
しかし、Minokは、そのオブジェクトの参照を渡していないことに注意してください。そのオブジェクトの新しいコピーを渡しますが、基本クラスを使用してプロセスでコピーします。
アラファンギオン2010

基本クラスでの保護されたコピー/割り当てとこの問題は解決されました。
2012年

1
あなたが正しい。抽象プラクティスクラスを使用するか、コピー/割り当てへのアクセスを制限することをお勧めします。しかし、一度見つかると簡単に見つけることができず、世話をするのを忘れがちです。スライスされた状態で仮想メソッドを呼び出す*これは、アクセス違反なしで逃げ出した場合に不思議なことが起こる可能性があります。
デュード

1
大学のC ++プログラミングコースで思い出したのは、作成したすべてのクラスについて、デフォルトのコンストラクター、コピーコンストラクター、代入演算子、およびデストラクタを記述する必要があったというベストプラクティスがあったことです。このようにして、クラスの作成中にコピーの作成などが必要な方法で行われることを確認しました。
Minok 2014

3

OK、オブジェクトのスライシングを説明する多くの投稿を読んだ後、試してみましょう。

メモリ破損を引き起こす可能性がある悪質なシナリオは次のとおりです。

  • クラスは、ポリモーフィック基本クラスに(偶然に、おそらくコンパイラによって生成された)割り当てを提供します。
  • クライアントは、派生クラスのインスタンスをコピーしてスライスします。
  • クライアントは、スライスされた状態にアクセスする仮想メンバー関数を呼び出します。

3

スライスとは、サブクラスのオブジェクトが値によって、または基本クラスオブジェクトを必要とする関数から渡されたり返されたりすると、サブクラスによって追加されたデータが破棄されることを意味します。

説明: 次のクラス宣言を検討してください。

           class baseclass
          {
                 ...
                 baseclass & operator =(const baseclass&);
                 baseclass(const baseclass&);
          }
          void function( )
          {
                baseclass obj1=m;
                obj1=m;
          }

ベースクラスのコピー関数は派生について何も知らないので、派生のベース部分のみがコピーされます。これは一般にスライスと呼ばれます。


1
class A 
{ 
    int x; 
};  

class B 
{ 
    B( ) : x(1), c('a') { } 
    int x; 
    char c; 
};  

int main( ) 
{ 
    A a; 
    B b; 
    a = b;     // b.c == 'a' is "sliced" off
    return 0; 
}

4
追加の詳細を教えていただけませんか?あなたの回答は、すでに投稿されている回答とどのように異なりますか?
Alexis Pigeon

2
これ以上の説明は悪くないと思います。
ルーパー:

-1

派生クラスオブジェクトが基本クラスオブジェクトに割り当てられると、派生クラスオブジェクトの追加の属性が基本クラスオブジェクトから切り取られます(破棄されます)。

class Base { 
int x;
 };

class Derived : public Base { 
 int z; 
 };

 int main() 
{
Derived d;
Base b = d; // Object Slicing,  z of d is sliced off
}

-1

派生クラスオブジェクトが基本クラスオブジェクトに割り当てられると、基本クラスに存在しないメンバーを除いて、派生クラスオブジェクトのすべてのメンバーが基本クラスオブジェクトにコピーされます。これらのメンバーは、コンパイラーによってスライスされます。これはオブジェクトスライスと呼ばれます。

次に例を示します。

#include<bits/stdc++.h>
using namespace std;
class Base
{
    public:
        int a;
        int b;
        int c;
        Base()
        {
            a=10;
            b=20;
            c=30;
        }
};
class Derived : public Base
{
    public:
        int d;
        int e;
        Derived()
        {
            d=40;
            e=50;
        }
};
int main()
{
    Derived d;
    cout<<d.a<<"\n";
    cout<<d.b<<"\n";
    cout<<d.c<<"\n";
    cout<<d.d<<"\n";
    cout<<d.e<<"\n";


    Base b = d;
    cout<<b.a<<"\n";
    cout<<b.b<<"\n";
    cout<<b.c<<"\n";
    cout<<b.d<<"\n";
    cout<<b.e<<"\n";
    return 0;
}

それは生成されます:

[Error] 'class Base' has no member named 'd'
[Error] 'class Base' has no member named 'e'

それは良い例ではないので、反対票を投じました。dをbにコピーする代わりにポインターを使用する場合も機能しません。その場合、dとeはまだ存在しますが、Baseにはそれらのメンバーがありません。あなたの例は、クラスが持っていないメンバーにアクセスできないことを示しているだけです。
Stefan Fabian

-2

私はスライスの問題に遭遇し、すぐにここに着陸しました。これに2セントを加算します。

「量産コード」(またはそれに近いもの)の例を見てみましょう。


アクションをディスパッチするものがあるとしましょう。たとえば、コントロールセンターのUI。
このUIは、現在ディスパッチ可能なもののリストを取得する必要があります。したがって、ディスパッチ情報を含むクラスを定義します。それを呼びましょうAction。したがって、Actionいくつかのメンバー変数があります。簡単にするために、a std::string nameとa である2を使用していstd::function<void()> fます。次に、void activate()それを実行するだけのfメンバーをです。

したがって、UIはstd::vector<Action>提供されます。次のようないくつかの関数を想像してみてください:

void push_back(Action toAdd);

これで、UIの観点から見た外観を確立しました。今のところ問題ありません。しかし、このプロジェクトに取り組んでいる他の人が突然、より多くの情報を必要とする特別なアクションがあると判断しましたActionオブジェクトに。どんな理由で。これはラムダキャプチャでも解決できます。この例は、コードから1-1で取得されたものではありません。

その男はAction、自分の味を追加するために派生しています。
彼は自作のクラスのインスタンスをpush_backその後プログラムは混乱します。

どうしたの?
あなたがそうかもしれないように推測している:オブジェクトは、スライスされています。

インスタンスからの余分な情報が失われ、f動作が未定義になる傾向があります。


この例が、AsとBが何らかの方法で派生していることについて話すとき、本当に物事を想像できない人々に光をもたらすことを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.