人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

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

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


●質問者: calotocen
●カテゴリ:コンピュータ
✍キーワード:C++ Class const うまい エイリアス
○ 状態 :終了
└ 回答数 : 3/3件

▽最新の回答へ

1 ● kawasaki
●27ポイント

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

どうやらそれは不可能なようです。(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;
}

2 ● tekk
●27ポイント

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

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

すみません。私自身は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


3 ● zifree
●26ポイント

・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);

}

};

関連質問


●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ