对没有友元函数的两个类私有操作

时间:2016-04-10 06:43:27

标签: c++ oop language-agnostic friend

考虑两个类BoxRabbit与一对一的简单关系 - 盒子最多可以包含一只兔子,兔子最多可以放在一个盒子里。

在伪代码中,我们有以下界面:

class Box
  - getRabbit():Rabbit
  - putRabbit(rabbit:Rabbit)

class Rabbit
  - getContainingBox():Box
  - putIntoBox(box:Box)

当然,我们希望保持不变:box.getRabbit().getContainingBox() == box,如果box不为空,则rabbit相同。

在C ++中,我们可以将函数putRabbitIntoBox(Rabbit* rabbit, Box* box)声明为BoxRabbit的朋友,并根据{{1}实现Box::putRabbitRabbit::putIntoBox }}。而且我没有看到这种方法有任何重大缺点(如果我错了,请纠正我):

putRabbitIntoBox

现在我们决定用Java(或C#,或任何没有class Box; class Rabbit; void putRabbitIntoBox(Rabbit* rabbit, Box* box); class Rabbit { public: Rabbit() :box(nullptr) {} Box* getContainingBox() { return box; } void putIntoBox(Box* box) { putRabbitIntoBox(this, box); } private: Box* box; friend void putRabbitIntoBox(Rabbit* rabbit, Box* box); }; class Box { public: Box() :rabbit(nullptr) {} Rabbit* getRabbit() { return rabbit; } void putRabbit(Rabbit* rabbit) { putRabbitIntoBox(rabbit, this); } private: Rabbit* rabbit; friend void putRabbitIntoBox(Rabbit* rabbit, Box* box); }; void putRabbitIntoBox(Rabbit* rabbit, Box* box) { if (rabbit && rabbit->box) { rabbit->box->rabbit = nullptr; } if (box && box->rabbit) { box->rabbit->box = nullptr; } if (rabbit) { rabbit->box = box; } if (box) { box->rabbit = rabbit; } } 函数的语言)实现相同的Box / Rabbit

有解决这个问题的惯用方法吗?

我知道可以通过从friend调用putRabbit来解决这个问题,反之亦然,使用一些保护代码,如下所示:

putIntoBox

但这对我来说太可怕了。我们有一个非常容易出错的递归“事物”,而不是一个有明确定义目的的函数。

2 个答案:

答案 0 :(得分:1)

您的约束是:

  1. Box可以将一个或零个Rabbit连接到
  2. Rabbit可以将一个或零个Box连接到
  3. 如果Box连接到Rabbit,则Rabbit连接到此Box,反之亦然
  4. Box和Rabbit都是同一级别的课程 - 您不希望将其中一个设为其他人的所有者......
  5. 所以 - 只需在Box和Rabbit中实现这些约束。第一件事 - 忘记为一切定义setter和getter - 只需实现你需要的函数 - 仅此而已。

    所以 - 让我们定义接口:

    class Box;
    class Rabbit {
    public:
        ~Rabbit() { disconnect(); }
        void connect(Box* box);
        Box* getMyBox() const { return box; }
    private:
        void disconnect();
        Box* box = nullptr;
    };
    class Box {
    public:
        ~Box() { disconnect(); }
        void connect(Rabbit* otherRabbit);
        Rabbit* getMyRabbit() const { return rabbit; }
    private:
        void disconnect();
        Rabbit* rabbit = nullptr;
    };
    

    正如您所看到的 - 通过在每个类中为其配合类定义一个指针,可以满足前两个约束。

    Sp唯一的左边约束是确保两个对象(Rabbit和Box)相互连接 - 或者根本没有连接:

    inline void Rabbit::connect(Box* otherBox)
    {
        if (box != otherBox)
        {
            disconnect();
            box = otherBox;
            if (box != nullptr)
                box->connect(this);
        }
    }
    
    inline void Rabbit::disconnect()
    {
        if (box != nullptr)
        {
            Box* oldBox = box;
            box = nullptr; // do this before calling disconnect on box - otherwise you get infinite recursion...
            oldBox->connect(nullptr);
        }
    }
    

    您必须在Box中定义类似的功能。关于ideone

    的工作示例

    在C ++中你可以使用模板 - 因为正如你所看到的 - 这两个类的定义方式非常相似......虽然我不能说模板是否会解决其他语言中的相同问题......

答案 1 :(得分:0)

你应该重新考虑你的课堂设计。在您的示例中,将Rabbit放入Box会更改两个类的状态。通常,可变状态是应尽可能避免的。在这种情况下,Box改变其状态是可以接受的,但为什么Rabbit也应该改变?

在两个类中都有相同的信息也会导致不一致。

因此,我只会定义:

class Box {
public:
    putRabbit(Rabbit r);
    Rabbit getRabbit();
...
}

Rabbit类没有与Box相关的任何方法。如果您将所有框存储在某个容器中,并且您实现了以下方法,那么您仍然可以找到Box Rabbit的内容:

Rabbit r;
...
Box b = allBoxes.findRabbitBox(r);