web-dev-qa-db-ja.com

基本クラスを抽象化し、コピーを構築、経験則

多くの場合、オブジェクトのインターフェイスを分離するための抽象基本クラスを用意することをお勧めします。

問題は、C++ではデフォルトでコピー構築であるIMHOがかなり壊れており、コピーコンストラクターがデフォルトで生成されていることです。

では、抽象基本クラスと派生クラスに生のポインタがある場合の落とし穴は何ですか?

class IAbstract
{
    ~IAbstract() = 0;
}

class Derived : public IAbstract
{
    char *theProblem;
    ...
}

IAbstract *a1 = new Derived();
IAbstract a2 = *a1;//???

そして、階層全体のコピー構築を完全に無効にしますか? IAbstractでコピー構成をプライベートとして宣言しますか?

抽象基本クラスの3つのルールはありますか?

9
Coder

抽象クラスのコピー構築は、ほとんどの場合、割り当て演算子と同様にプライベートにする必要があります。

抽象クラスは、定義により、ポリモーフィック型になります。そのため、インスタンスが使用しているメモリの量がわからないため、安全にコピーまたは割り当てることができません。実際には、スライスのリスクがあります: https://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c

C++では、ポリモーフィック型を値で操作してはなりません。参照またはポインター(または任意のスマートポインター)で操作します。

これが、Javaがオブジェクトを参照によってのみ操作可能にした理由であり、C#とDがクラスと構造体の間の分離を持っている理由です(最初のものはポリモーフィックで参照タイプで、2つ目は非ポリモーフィックおよび値型)。

6
deadalnix

もちろん、それを保護して空にして、派生クラスが選択できるようにすることもできます。ただし、より一般的には、コードは禁止されていますとにかく純粋な仮想関数があるため、IAbstract-をインスタンス化することは不可能であるためです。そのため、これは通常は問題ではありません。インターフェイスクラスはインスタンス化できないため、コピーすることはできません。派生クラスは、必要に応じてコピーを禁止または続行できます。

8
DeadMG

Ctorと割り当てをプライベートにする(またはそれらをC++ 11で= deleteと宣言する)と、コピーが無効になります。

ここでのポイントは、それを行う必要がある場所です。コードを維持するために、IAbstractは問題になりません。 (あなたがしたことをすることに注意してください、あなたは_*a1_ IAbstractサブオブジェクトをa2に割り当て、Derivedへの参照を失います。値の割り当ては多態的ではありません)

問題は_Derived::theproblem_で発生します。 Derivedを別のDerivedにコピーすると、実際には、共有するように設計されていない_*theproblem_データが共有される場合があります(デストラクタで_delete theproblem_を呼び出す可能性のあるインスタンスが2つあります)。

その場合、Derivedはコピー不可で割り当て不可でなければなりません。もちろん、IAbstractのコピーを非公開にすると、Derivedのデフォルトのコピーでそれが必要になるため、Derivedもコピーできなくなります。ただし、IAbtract copyを呼び出さずに独自のDerived::Derived(const Derived&)を定義した場合でも、それらをコピーできます。

問題はDerivedにあり、ソリューションはDerivedにとどまる必要があります。それがポインターまたは参照によってのみアクセスされる動的のみのオブジェクトでなければならない場合、それ自体がDerivedである必要があります。

_class Derived
{
    ...
    Derived(const Derived&) = delete;
    Derived& operator=(const Derived&) = delete;
};
_

基本的に、Derivedクラスの設計者(Derivedの動作方法とtheproblemの管理方法を知っている必要があります)が、割り当てとコピーの処理方法を決定します。

2