例外到三个规则? [英] Exception to the Rule of Three?

查看:207
本文介绍了例外到三个规则?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经阅读了很多关于C ++的三维规则。许多人发誓。但是当规则被声明时,它几乎总是包括一个词通常,可能或可能,表示有例外。我没有看到很多讨论这些特殊情况可能是什么 - 三原则规则不成立的情况,或至少在坚持它不提供任何优势。


$ b 我认为在下面描述的情况下,需要一个显式定义的复制构造函数和复制赋值操作符,但默认(隐式生成)析构函数将正常工作。这里是我的情况:



我有两个类,A和B.这里有一个是A. B是A的朋友。A包含一个B对象。 B包含一个A指针,用于指向拥有B对象的A对象。 B使用此指针来操纵A对象的私有成员。 B从不实例化,除了在A构造函数。像这样:

  // Ah 

#includeBh
$ b b class A
{
private:
B b;
int x;
public:
friend class B;
A(int i = 0)
:b(this){
x = i;
};
};

和...

  // Bh 

#ifndef B_H //预处理器转义以避免无限循环
#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();
//此方法必须在下面定义
//因为A的成员不能在这里访问
};

#includeA.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对象。我们将此代码添加到 A类

  public:
A(const A& other)
{
//首先我们复制默认复制构造函数的功能
x = other.x;
b = other.b;
// b.y已经被正确复制
// b.ap已经被复制,因此指向'other'
b.init(this); //这个额外的步骤是必要的
};

出于同样的原因,复制赋值运算符是必要的,默认复制赋值运算符的功能,然后调用 b.init(this);



需要一个显式析构函数; ergo这种情况是三原则的例外。对不对?

解决方案

不要担心三个规则。规则不是盲目地服从;他们在那里让你想。你以为。你的结论是析构函数不会这样做。所以不要写一个。该规则存在,因此您不会忘记写出析构函数,泄露资源。



同样,此设计创建了一个潜在的B :: ap错了。这是一类潜在的错误,如果这些是单个类,或者以更强大的方式绑在一起,可以消除。


I've read a lot about the C++ Rule of Three. Many people swear by it. But when the rule is stated, it almost always includes a word like "usually," "likely," or "probably," indicating that there are exceptions. I haven't seen much discussion of what these exceptional cases might be -- cases where the Rule of Three does not hold, or at least where adhering to it doesn't offer any advantage.

My question is whether my situation is a legitimate exception to the Rule of Three. I believe that in the situation I describe below, an explicitly defined copy constructor and copy assignment operator are necessary, but the default (implicitly generated) destructor will work fine. Here is my situation:

I have two classes, A and B. The one in question here is A. B is a friend of A. A contains a B object. B contains an A pointer which is intended to point to the A object that owns the B object. B uses this pointer to manipulate private members of the A object. B is never instantiated except in the A constructor. Like this:

// A.h

#include "B.h"

class A
{
private:
    B b;
    int x;
public:
    friend class B;
    A( int i = 0 )
    : b( this ) {
        x = i;
    };
};

and...

// 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

Why would I set up my classes like that? I promise, I have good reasons. These classes actually do way more than what I've included here.

So the rest is easy, right? No resource management, no Big Three, no problem. Wrong! The default (implicit) copy constructor for A will not suffice. If we do this:

A a1;
A a2(a1);

we get a new A object a2 that is identical to a1, meaning that a2.b is identical to a1.b, meaning that a2.b.ap is still pointing to a1! This is not what we want. We must define a copy constructor for A that duplicates the functionality of the default copy constructor and then sets the new A::b.ap to point to the new A object. We add this code to 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
    };

A copy assignment operator is necessary for the same reason and would be implemented using the same process of duplicating the functionality of the default copy assignment operator and then calling b.init( this );.

But there is no need for an explicit destructor; ergo this situation is an exception to the Rule of Three. Am I right?

解决方案

Don't worry so much about the "Rule of Three". Rules aren't there to be obeyed blindly; they're there to make you think. You've thought. And you've concluded that the destructor wouldn't do it. So don't write one. The rule exists so that you don't forget to write the destructor, leaking resources.

All the same, this design creates a potential for B::ap to be wrong. That's an entire class of potential bugs that could be eliminated if these were a single class, or were tied together in some more robust way.

这篇关于例外到三个规则?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆