web-dev-qa-db-ja.com

C ++派生クラスコピーコンストラクター

派生クラスDrinkを持つ抽象クラスCoffeeがある場合、Coffeeのコピーコンストラクターはどのようになりますか? Type Coffeeの参照をパラメーターとして受け入れる場合、次のようにインスタンス化されたインスタンスを渡すことはできません:Drink* coffee = new Coffee(..);しかし、Drinkでは、たとえば、Drinkが実際にCoffeeまたはTeaタイプであるかどうかを知る手段はありません。

3
FoxTrod

コピーコンストラクターは、まさにその定義上、常に宣言されたのとまったく同じ型への参照を取ります。したがって、CoffeeのコピーコンストラクターシグネチャはCoffee(const Coffee&)になります。他のコンストラクターはコピーコンストラクターではありません。

コピーコンストラクターは通常のctorとして使用できますが、コピー割り当て_Coffe c = other_coffee_の言語によって暗黙的に使用されます。これは、Coffee c(other_coffee)と同等であり、値によってオブジェクトを関数に渡します。 、例えばdrink(Coffee)にはコピーが必要です。

DrinkCoffeeまたは他の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()。もちろん、そのようなことに興味がある場合は、生のポインタを使用することもできます。

5
amon

しかし、私がドリンクへの言及を受け入れると、たとえばドリンクが実際にコーヒーまたはティーのタイプであるかどうかを知る手段がありません。

スーパークラスCoffeeからサブクラス化する任意のオブジェクトからサブクラス(Drink)をコピーすることはできません。したがって、答えは、コピーコンストラクタが同じクラス(Coffee)を受け入れるように制限する必要があるということです。

Drink *coffee = new Coffee(...);から目的のコピーコンストラクターを呼び出すことができないことに注意してください。ただし、Coffee coffee(...);を実行すると、コピーコンストラクターを使用できます。

コピーコンストラクターの主な用途の1つは、オブジェクト(ポインターや参照ではなくオブジェクトの値)を関数に渡して返すときに、コンパイラーがそれを自動的に呼び出すことです。そのため、コピーコンストラクターは、値および特定のコンパイル時の既知の型があると推定される値をコピーするためのものです。

したがって、ポインタによるオブジェクトとサブクラス化/ポリモーフィズムによる値によるコピーの使用を検討しています。これはかなり奇妙な領域です。値によるコピーメカニズムは、コンパイル時に、結果のエンティティコピーの目的のタイプ(および割り当てるサイズ)がわかっていることを前提としています。

代わりに、オブジェクトの静的タイプまたはコンパイル時タイプではなく、オブジェクトの動的タイプまたは実行時タイプを複製する必要があると思います。オブジェクトの動的タイプを複製するには、仮想複製メソッドを導入する必要があります。ただし、コピーコンストラクターから仮想クローンメソッドを呼び出すことができます。これにより、必要なものが提供される場合があります。

「仮想コピーコンストラクター」パターンを検索できます。

2
Erik Eidt

変換関数はこれに対する答えです

コピーctorは常にそれ自体の型を引数として受け入れるため、Drinkのc​​opy ctorにはCoffeeを受け入れるスコープがありません。

class Coffee: public Drink
{
    ...
public:
    Coffee(const Coffee& other);
    ...
};

それでもドリンク(おそらくはコーヒー)からコーヒーを初期化する場合は、変換演算子を使用できます。 DrinkCoffeeではない場合の対処方法を決定する必要があることに注意してください。以下のコードで例外をスローしたので、ソースデータが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つの仮想関数がある場合に機能することに注意してください。

。 。 。

0
Ramakant