web-dev-qa-db-ja.com

派生クラスオブジェクトを基本クラス変数に保存する

いくつかのクラスのインスタンスをベクターに保存したいと思います。すべてのクラスは同じ基本クラスから継承するため、これは可能です。

このプログラムを想像してください:

#include <iostream>
#include <vector>
using namespace std;

class Base
{
    public:
    virtual void identify ()
    {
        cout << "BASE" << endl;
    }
};

class Derived: public Base
{
    public:
    virtual void identify ()
    {
        cout << "DERIVED" << endl;
    }
};

int main ()
{
    Derived derived;

    vector<Base> vect;
    vect.Push_back(derived);

    vect[0].identify();
    return 0;
}

「identify」メソッドは仮想であるため、「DERIVED」が出力されると予想していました。代わりに、「vect [0]」は「Base」インスタンスのようで、印刷されます

ベース

どういうわけかこれを行うことができる独自のコンテナー(おそらくベクターから派生)を書くことができると思います(おそらくポインターのみを保持します...)。これを行うためのC++風のメソッドが他にあるかどうかを尋ねたかっただけです。そして、私は完全にベクトル互換にしたいと思います(他のユーザーが私のコードを使用する必要がある場合にのみ便利です)。

52
drakide

表示されているのはオブジェクトのスライスです。
Derivedクラスのオブジェクトを、Baseクラスのオブジェクトを格納することになっているベクターに格納しています。これにより、オブジェクトのスライシングが発生し、格納されているオブジェクトの派生クラス固有のメンバーが切り取られ、オブジェクトが格納されますベクトルは基本クラスのオブジェクトとして機能します。

解決策:

Baseクラスのオブジェクトへのポインターをベクターに保存する必要があります。

vector<Base*> 

Baseクラスへのポインターを保存することにより、スライスは行われず、目的の多態的な動作も実現できます。
C++ishこれを行う方法、正しいアプローチは、適切なスマートポインターを使用することです。ベクター。これにより、メモリを手動で管理する必要がなくなります。[〜#〜] raii [〜#〜]自動的にそれを行います。

64
Alok Save

スライスが発生しています。ベクターはderivedオブジェクトをコピーし、Base型の新しいオブジェクトが挿入されます。

5
Andre

TL; DR:パブリックにコピー/移動可能なクラスから継承しないでください。


実際には、コンパイル時にオブジェクトのスライスを防ぐことができます。このコンテキストでは、ベースオブジェクトはコピーできません。

ケース1:抽象ベース

ベースが抽象的である場合、インスタンス化できないため、スライスを体験できません。

ケース2:具体的なベース

ベースが抽象的でない場合は、コピーできます(デフォルト)。次の2つの選択肢があります。

  • コピーを完全に防ぐ
  • 子供のみにコピーを許可する

注:C++ 11では、移動操作によって同じ問題が発生します。

// C++ 03, prevent copy
class Base {
public:

private:
    Base(Base const&);
    void operator=(Base const&);
};

// C++ 03, allow copy only for children
class Base {
public:

protected:
    Base(Base const& other) { ... }
    Base& operator=(Base const& other) { ...; return *this; }
};

// C++ 11, prevent copy & move
class Base {
public:
    Base(Base&&) = delete;
    Base(Base const&) = delete;
    Base& operator=(Base) = delete;
};

// C++ 11, allow copy & move only for children
class Base {
public:

protected:
    Base(Base&&) = default;
    Base(Base const&) = default;
    Base& operator=(Base) = default;
};
5
Matthieu M.

vector<Base*>を使用して保存します。 vector<Base>と言うと、スライスが発生します。

これは、ベクターからポインターを削除した後、実際のオブジェクトを自分で削除する必要があることを意味しますが、それ以外の場合は問題ありません。

3
Vlad
// Below is the solution by using vector<Based*> vect,
// Base *pBase , and initialized pBase with
// with the address of derived which is
// of type Derived

#include <iostream>
#include <vector>

using namespace std;

class Base
{

public:

virtual void identify ()
{
    cout << "BASE" << endl;
}
};

class Derived: public Base
{
public:
virtual void identify ()
{
    cout << "DERIVED" << endl;
}
};

int main ()

{
Base *pBase; // The pointer pBase of type " pointer to Base"
Derived derived;
// PBase is initialized with the address of derived which is
// of type Derived

pBase = & derived;
// Store pointer to object of Base class in the vector:
vector<Base*> vect;
// Add an element to vect using pBase which is initialized with the address 
// of derived
vect.Push_back(pBase);
vect[0]->identify();
return 0;
}
0
Van Tran