派生クラスDrink
を持つ抽象クラスCoffee
がある場合、Coffeeのコピーコンストラクターはどのようになりますか? Type Coffee
の参照をパラメーターとして受け入れる場合、次のようにインスタンス化されたインスタンスを渡すことはできません:Drink* coffee = new Coffee(..);
しかし、Drink
では、たとえば、Drink
が実際にCoffee
またはTea
タイプであるかどうかを知る手段はありません。
コピーコンストラクターは、まさにその定義上、常に宣言されたのとまったく同じ型への参照を取ります。したがって、Coffee
のコピーコンストラクターシグネチャはCoffee(const Coffee&)
になります。他のコンストラクターはコピーコンストラクターではありません。
コピーコンストラクターは通常のctorとして使用できますが、コピー割り当て_Coffe c = other_coffee
_の言語によって暗黙的に使用されます。これは、Coffee c(other_coffee)
と同等であり、値によってオブジェクトを関数に渡します。 、例えばdrink(Coffee)
にはコピーが必要です。
Drink
がCoffee
または他のDrink
サブクラスである可能性がある場合、これは必然的にDrink
へのポインタまたは参照があることを意味します。このような場合、通常はポイントされた値のコピーを作成する必要はありません。ポインタをコピーするだけです。
実際にDrink
をコピーする必要がある場合は、あらゆる種類のDrink
がそれ自体を複製できるように、仮想メソッドを作成する必要があります。
_class Drink {
public:
virtual ~Drink() = default;
virtual std::unique_ptr<Drink> clone() const = 0;
...
};
class Coffee : public Drink {
...
Coffee(Coffee const&);
std::unique_ptr<Drink> clone() const override;
};
std::unique_ptr<Drink> Coffee::clone() const {
return std::make_unique<Coffee>(*this); // requires C++ 14
}
_
次に:std::unique_ptr<Drink const> a = ...; std::unique_ptr<Drink const> b = a->clone()
。もちろん、そのようなことに興味がある場合は、生のポインタを使用することもできます。
しかし、私がドリンクへの言及を受け入れると、たとえばドリンクが実際にコーヒーまたはティーのタイプであるかどうかを知る手段がありません。
スーパークラスCoffee
からサブクラス化する任意のオブジェクトからサブクラス(Drink
)をコピーすることはできません。したがって、答えは、コピーコンストラクタが同じクラス(Coffee
)を受け入れるように制限する必要があるということです。
Drink *coffee = new Coffee(...);
から目的のコピーコンストラクターを呼び出すことができないことに注意してください。ただし、Coffee coffee(...);
を実行すると、コピーコンストラクターを使用できます。
コピーコンストラクターの主な用途の1つは、オブジェクト(ポインターや参照ではなくオブジェクトの値)を関数に渡して返すときに、コンパイラーがそれを自動的に呼び出すことです。そのため、コピーコンストラクターは、値および特定のコンパイル時の既知の型があると推定される値をコピーするためのものです。
したがって、ポインタによるオブジェクトとサブクラス化/ポリモーフィズムによる値によるコピーの使用を検討しています。これはかなり奇妙な領域です。値によるコピーメカニズムは、コンパイル時に、結果のエンティティコピーの目的のタイプ(および割り当てるサイズ)がわかっていることを前提としています。
代わりに、オブジェクトの静的タイプまたはコンパイル時タイプではなく、オブジェクトの動的タイプまたは実行時タイプを複製する必要があると思います。オブジェクトの動的タイプを複製するには、仮想複製メソッドを導入する必要があります。ただし、コピーコンストラクターから仮想クローンメソッドを呼び出すことができます。これにより、必要なものが提供される場合があります。
「仮想コピーコンストラクター」パターンを検索できます。
コピーctorは常にそれ自体の型を引数として受け入れるため、Drink
のcopy ctorにはCoffee
を受け入れるスコープがありません。
class Coffee: public Drink
{
...
public:
Coffee(const Coffee& other);
...
};
それでもドリンク(おそらくはコーヒー)からコーヒーを初期化する場合は、変換演算子を使用できます。 Drink
がCoffee
ではない場合の対処方法を決定する必要があることに注意してください。以下のコードで例外をスローしたので、ソースデータがDrink
ではない場合、Coffee
オブジェクトの作成を続行したくありません。
class Coffee: public Drink
{
...
public:
//Standard copy-ctor
Coffee(const Coffee& other);
//Conversion function
Coffee(const Drink& drink)
{
const Coffee& other = dynamic_cast<const Coffee&>(drink);
//this->x = other.x;
//this->y = other.y;
}
};
このようにして、渡された参照が別のタイプ(たとえば、DrinkまたはTea)である場合、std::bad_cast
例外がスローされ、オブジェクトの作成が中止されます。
。 。 。
//d1: drink reference pointing to object of type 'Drink' OR 'Tea' OR 'Coffee'
try
{
Coffee c1(d1);
cout << "Created coffee from drink" << endl;
}
catch(bad_cast& ex)
{
cout << "Exception : " << ex.what() << endl;
}
上記のコードは、Coffee
オブジェクトに対しては正常に機能し、他のタイプに対しては例外をスローします。
別の方法で作業したい場合は、Actorで例外を処理してそこから移動できます。 通常、ctorで例外を処理することは、正当な理由があるために避けられます。
dynamic_castは、ポリモーフィック階層、つまり基本クラスに少なくとも1つの仮想関数がある場合に機能することに注意してください。
。 。 。