C++のポリモーフィズムに関する質問です。

あるクラスの派生オブジェクトをコンストラクタの引数として受け取り,それを保持するクラスを作ろうとしています。オブジェクトを保持するクラスは「外部からオブジェクトを受け取った後に外部でそのオブジェクトの内容を変更しても,変更が保持しているオブジェクトに影響を与えないこと」という要件を満たすように実装したいのですが,うまい実装が見つかりません。単にオブジェクトをコピーすればいいのですが,コピーコンストラクタやエイリアスを使った方法ではスライシングが発生したり要件を満たせなかったりと実装に悩んでいます。
現状,下記コードのようにclone()というメンバ関数を用意してオブジェクトをコピーしていますが,ポリモーフィズムを使用するたびに専用のメンバ関数を用意したくありません。もっとスマートな実装方法をご存知の方がいらっしゃいましたら,ご教示頂けませんでしょうか。

class FruitBasket {
private:
  Fruit* m_fruit;
public:
  // fruit の値をコピーしたい。
  FruitBasket(const Fruit& fruit) {
    // clone() は fruit のコピーを返すメンバ関数
    m_fruit = fruit.clone();
  }
};

回答の条件
  • 1人2回まで
  • 登録:
  • 終了:2009/12/30 13:45:02
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答3件)

id:phero No.1

回答回数55ベストアンサー獲得回数9

ポイント27pt

この問題は親クラスから子クラスのサイズを動的に知ることができれば解決な気がするのですが、

どうやらそれは不可能なようです。(sizeofの返り値がコンパイル時に決定されてしまうため。)

以下のようにすると各子クラスにclone()関数を実装せずに済みますが、

代わりにGetSize()関数を実装しなければならなくなります。

私の力では継承無しでご要望を満たすのはC++では無理だという結論になりました(笑)。

class Fruit {
protected:
    int m_value;
public:
    Fruit() : m_value(0) {
        printf("called Fruit::Fruit()\n");
    }
    virtual ~Fruit() {
        printf("called Fruit::~Fruit()\n");
    }
    virtual const char* GetType() const {
        return "Fruit";
    }
    virtual int GetSize() const {
        return sizeof(*this);
    }
    void set(int new_value) {
        m_value = new_value;
    }
    
    virtual void print() const {
        printf("Fruit::print() values are (%d)\n", m_value);
    }
};

class Apple : public Fruit {
private:
    int m_color;
public:
    Apple() : m_color(0x0000ff) {
        printf("called Apple::Apple()\n");
        m_value = 10;
    }
    virtual ~Apple() {
        printf("called Apple::~Apple()\n");
    }
    virtual const char* GetType() const {
        return "Apple";
    }
    virtual int GetSize() const {
        return sizeof(*this);
    }
    virtual void print() const {
        printf("Apple::print() values are (%d, %d)\n", m_value, m_color);
    }
};

class FruitBasket {
private:
    Fruit* m_fruit;
public:
    FruitBasket(const Fruit& fruit) {
        m_fruit = (Fruit*)malloc(fruit.GetSize());
        memcpy(m_fruit, &fruit, fruit.GetSize());
    }
    ~FruitBasket() {
        free(m_fruit);
    }
    void print() {
        printf("Basket includes %s - ", m_fruit->GetType());
        m_fruit->print();
    }
};

int main( int argc, char** argv )
{
    Fruit *apple = new Apple();
    apple->print();
    
    FruitBasket basket(*apple);
    basket.print();
    
    printf("----- オブジェクトに変更を加える -----\n");
    apple->set(999);
    
    apple->print();
    basket.print();
    
    delete apple;
}
id:tekk No.2

回答回数5ベストアンサー獲得回数1

ポイント27pt

オブジェクトをファイルなどに保存したり、ファイルに保存したファイルからオブジェクトを作る技術でシリアライズ(永続化)というものがあります。

関係のない技術のように思われるかもしれませんが、ファイルではなくメモリ上でシリアライズしたデータを保持すればコピーの代わりに使うことができます。

すみません。私自身はC++でシリアライズしたことは有りませんので、

次のサイトを参考にしてみてください。


Boost::Serialization

http://hw001.gate01.com/eggplant/tcf/cpp/boost_serialization.htm...


今回は利用できないかもしれませんが、

MicroSoftのC#、VB.NETでは以下の方法で対応できます。

VC++.NETでも使えると思います。


オブジェクトのコピー。ICloneableインタフェース、MemberWiseClone、シリアライズを利用したインスタンスのコピー。

http://d.hatena.ne.jp/tekk/20091012/1255362429

id:zifree No.3

回答回数175ベストアンサー獲得回数6

ポイント26pt

・Fruitにprotectedコンストラクタを用意して、メンバ変数のコピー処理をその中に記述します。

・FruitBasketはFruitを継承した内部クラス(CFruit)を持っています。

・CFruitのコンストラクタは、上で作った親クラスFruitのprotectedコンストラクタを呼びだします。

・欠点としては他のクラスがFruitを継承してしまうと、そのクラスからprotectedコンストラクタが見えてしまいます。

class Fruit {

//中略

protected:

  Fruit(const Fruit& fruit); //fruitのメンバ変数の内容を全部コピー

//以下略

};

class FruitBasket {

private:

  class CFruit : public Fruit {

    CFruit(const Fruit& fruit) : Fruit(fruit){};

    virtual ~CFruit();

  };

  Fruit* m_fruit;

public:

  FruitBasket(const Fruit& fruit) {

    m_fruit = new CFruit(fruit);

  }

};

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません