我已经阅读了很多关于 C++规则三的内容。许多人对此发誓。但是当规定规则时,它几乎总是包含“通常”、“可能”或“可能”这样的词,表示存在例外情况。我还没有看到太多关于这些例外情况可能是什么的讨论——三法则不成立的情况,或者至少坚持三法则没有任何优势的情况。
我的问题是我的情况是否是三原则的合法例外。我相信在我下面描述的情况下,显式定义的复制构造函数和复制赋值运算符是必要的,但默认的(隐式生成的)析构函数可以正常工作。这是我的情况:
我有两个类,A 和 B。这里有问题的是 A。B 是 A 的朋友。A 包含一个 B 对象。B 包含一个 A 指针,该指针旨在指向拥有 B 对象的 A 对象。B 使用此指针来操作 A 对象的私有成员。除了在 A 构造函数中之外,B 永远不会被实例化。像这样:
// 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);
我们得到一个新的 A 对象a2
,它与 相同a1
,意思a2.b
是相同的a1.b
,意思a2.b.ap
是仍然指向a1
!这不是我们想要的。我们必须为 A 定义一个复制构造函数,它复制默认复制构造函数的功能,然后将 new 设置A::b.ap
为指向新的 A 对象。我们将此代码添加到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 );
.
但是不需要显式的析构函数;因此,这种情况是三法则的一个例外。我对吗?