我已经阅读了大量关于C++ 三法则的内容,很多人都对它深信不疑。但是当这个规则被说明时,几乎总会包含像“通常”,“可能”或“大概”之类的词语,表明有例外情况。我还没有看到过关于这些特殊情况的讨论--即三法则不适用的情况,或者至少遵循它没有任何优势的情况。
我的问题是,我的情况是否是三法则的合法例外。 我认为,在我描述的情况下,需要显式定义复制构造函数和复制赋值运算符,但默认(隐式生成的)析构函数可以正常工作。以下是我的情况:
我有两个类,A和B。这里涉及的是A。B是A的朋友。A包含一个B对象。B包含一个指向A对象的指针,该指针旨在指向拥有B对象的A对象。B使用此指针来操作A对象的私有成员。除了在A构造函数中,B永远不会被实例化。就像这样:
我们得到了一个新的A对象
我有两个类,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定义一个复制构造函数,该函数复制默认复制构造函数的功能,然后将新的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);
。
但是不需要显式析构函数,因此这种情况是三大法则的例外。我是正确的吗?