C++ Rule of Threeについて多くのことを読みました。多くの人がそれを誓います。しかし、ルールが述べられている場合、ほとんどの場合、例外があることを示す「通常」、「可能性が高い」、「おそらく」などの単語が含まれています。これらの例外的なケースが何であるかについての議論はあまり見たことがありません.3つのルールが成り立たないケース、または少なくともそれを順守しても利点がないケースです。
私の質問は、私の状況が 3 つのルールの正当な例外であるかどうかです。以下で説明する状況では、明示的に定義されたコピー コンストラクターとコピー代入演算子が必要ですが、既定の (暗黙的に生成された) デストラクタは正常に機能します。これが私の状況です:
A と B の 2 つのクラスがあります。ここで問題になっているのは A です。B は A のフレンドです。A には B オブジェクトが含まれています。B には、B オブジェクトを所有する A オブジェクトを指すことを意図した A ポインターが含まれています。B は、このポインターを使用して、A オブジェクトのプライベート メンバーを操作します。B は、A コンストラクター以外でインスタンス化されることはありません。このような:
// A.h
#include "B.h"
class A
{
private:
B b;
int x;
public:
friend class B;
A( int i = 0 )
: b( this ) {
x = i;
};
};
と...
// B.h
#ifndef B_H // preprocessor escape to avoid infinite #include loop
#define B_H
class A; // forward declaration
class B
{
private:
A * ap;
int y;
public:
B( A * a_ptr = 0 ) {
ap = a_ptr;
y = 1;
};
void init( A * a_ptr ) {
ap = a_ptr;
};
void f();
// this method has to be defined below
// because members of A can't be accessed here
};
#include "A.h"
void B::f() {
ap->x += y;
y++;
}
#endif
なぜそのようなクラスを設定するのでしょうか? 私は約束します、私には正当な理由があります。これらのクラスは、実際には、ここに含めた以上のことを行います。
残りは簡単ですよね?リソース管理もビッグスリーも問題ありません。違う!A の既定の (暗黙的な) コピー コンストラクターでは十分ではありません。これを行う場合:
A a1;
A a2(a1);
a2
と同一の新しい A オブジェクトを取得します。これは、と同一であり、まだ を指していることを意味しa1
ます。これは私たちが望んでいるものではありません。デフォルトのコピー コンストラクターの機能を複製し、新しい A オブジェクトを指すようにnew を設定する A のコピー コンストラクターを定義する必要があります。このコードを に追加します。a2.b
a1.b
a2.b.ap
a1
A::b.ap
class A
public:
A( const A & other )
{
// first we duplicate the functionality of a default copy constructor
x = other.x;
b = other.b;
// b.y has been copied over correctly
// b.ap has been copied over and therefore points to 'other'
b.init( this ); // this extra step is necessary
};
コピー代入演算子は同じ理由で必要であり、デフォルトのコピー代入演算子の機能を複製してから を呼び出す同じプロセスを使用して実装されますb.init( this );
。
ただし、明示的なデストラクタは必要ありません。したがって、この状況は 3 つのルールの例外です。私は正しいですか?