あるクラスの派生オブジェクトをコンストラクタの引数として受け取り,それを保持するクラスを作ろうとしています。オブジェクトを保持するクラスは「外部からオブジェクトを受け取った後に外部でそのオブジェクトの内容を変更しても,変更が保持しているオブジェクトに影響を与えないこと」という要件を満たすように実装したいのですが,うまい実装が見つかりません。単にオブジェクトをコピーすればいいのですが,コピーコンストラクタやエイリアスを使った方法ではスライシングが発生したり要件を満たせなかったりと実装に悩んでいます。
現状,下記コードのようにclone()というメンバ関数を用意してオブジェクトをコピーしていますが,ポリモーフィズムを使用するたびに専用のメンバ関数を用意したくありません。もっとスマートな実装方法をご存知の方がいらっしゃいましたら,ご教示頂けませんでしょうか。
class FruitBasket {
private:
Fruit* m_fruit;
public:
// fruit の値をコピーしたい。
FruitBasket(const Fruit& fruit) {
// clone() は fruit のコピーを返すメンバ関数
m_fruit = fruit.clone();
}
};
この問題は親クラスから子クラスのサイズを動的に知ることができれば解決な気がするのですが、
どうやらそれは不可能なようです。(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; }
オブジェクトをファイルなどに保存したり、ファイルに保存したファイルからオブジェクトを作る技術でシリアライズ(永続化)というものがあります。
関係のない技術のように思われるかもしれませんが、ファイルではなくメモリ上でシリアライズしたデータを保持すればコピーの代わりに使うことができます。
すみません。私自身はC++でシリアライズしたことは有りませんので、
次のサイトを参考にしてみてください。
Boost::Serialization
http://hw001.gate01.com/eggplant/tcf/cpp/boost_serialization.htm...
今回は利用できないかもしれませんが、
MicroSoftのC#、VB.NETでは以下の方法で対応できます。
VC++.NETでも使えると思います。
オブジェクトのコピー。ICloneableインタフェース、MemberWiseClone、シリアライズを利用したインスタンスのコピー。
・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);
}
};
コメント(1件)
virtual ~CFruit();
は余計なコードでした。