C++友元类

6

我知道有很多关于C++友元类的问题。然而,我的问题与特定情景有关。在给出的代码中,使用友元类是否合适?

class Software
{
    friend class SoftwareProducer;

    SoftwareProducer* m_producer;
    int m_key;
    // Only producers can produce software
    Software(SoftwareProducer* producer) : m_producer(producer) { }

public:
    void buy()
    {
        m_key = m_producer->next_key();
    }
};

class SoftwareProducer
{
    friend class Software;

public:
    Software* produce()
    {
        return new Software(this);
    }

private:
    // Only software from this producer can get a valid key for registration
    int next_key()
    {
        return ...;
    }
};

感谢您的来信,
此致
敬礼!
最好的问候,
4个回答

8

当然,这是非常合理的。基本上你所做的与工厂模式非常相似。我认为没有问题,因为你的代码似乎意味着每个Software对象都应该有一个指向其创建者的指针。

虽然通常情况下,你可以避免像SoftwareProducer这样的"Manager"类,而只需在Software中使用静态方法。因为很可能只会有一个SoftwareProducer。也许可以像这样实现:

class Software {
private:
    Software() : m_key(0) { /* whatever */ }
public:
    void buy() { m_key = new_key(); }
public:
    static Software *create() { return new Software; }
private:
    static int new_key() { static int example_id = 1; return example_id++; }
private:
    int m_key;
};

那么你可以这样做:
Software *soft = Software::create();
soft->buy();

当然,如果您计划拥有多个SoftwareProducer对象,则您所做的似乎是合适的。

2

1

我认为将SoftwareProducer作为Software的朋友是可接受的,但我看不出为什么Software必须成为SoftwareProducer类的朋友。这是它们之间不必要的依赖关系。您可以将关键参数作为Software类的构造函数参数。此外,您可能希望将Software类的析构函数设为私有,以便除SoftwareProducer之外的任何人都无法销毁它们。


我将Software作为SoftwareProducer的朋友,以便访问私有的next_key方法。 - Alex
难道不是反过来吗?为了允许从SoftwareProducer调用Software的私有构造函数,您需要将SoftwareProducer设置为Software的友元,这是有意义的。但是,您还将Software设置为SoftwateProducer的友元,以便可以调用next_key()方法,我觉得这是不必要的。 - Naveen
如果next_key方法是私有的,那么软件实例还能以其他方式访问它的生产者吗? - Alex
这就是为什么我说你可以将密钥作为Software类的构造函数参数传递。 - Naveen
我之前没有提到,但是密钥很可能会改变。为什么软件会带有密钥呢?只有客户付款时才有意义 :) 但是我跑题了。 - Alex

1

我不想讨论友谊问题,但我不喜欢循环依赖。软件依赖于 SoftwareProducer,而 SoftwareProducer 又依赖于 Software…… 我会尝试重构。

此外,请注意友谊关系会将内部成员全部暴露给 所有 其他类的实例。这就跟你在 next_key 中所说的一样:

next_key 设为私有可以阻止任何类对其进行调用,但一旦打开给 Software 类,所有软件都可以调用所有的 SoftwareProducersnext_key


+1,你在 next_key() 方面是技术上正确的 - 任何软件都可以调用它 - 但它也不会带来安全风险,因为你对软件的定义有控制权,而友谊关系不具有传递性或继承性。此外,在可能的情况下最好避免循环依赖,但有时它们是必要的 - 你能否在这种情况下提出更好的替代方案? - j_random_hacker
确实,我不喜欢循环依赖,这就是为什么我发表了这个问题。我同意这是一个(故意)复杂的例子。 - Alex
1
大多数循环依赖关系可以通过定义接口并依赖于它们来消除。现在,一旦您开始使用private+friend来控制访问,这种方式将变得更加困难。我可能会将购买操作从Software中移出,并将其放入外部Dealer/Shop或甚至SoftwareProducer中,并将访问控制推入逻辑而不是类型系统中。 - David Rodríguez - dribeas
@dribeas:听起来不错。在这种情况下,我喜欢接口,因为它们可以减少耦合--软件和软件生产者都依赖于一个接口而不是彼此(一个实现,另一个作为客户端)--但是,是的,这使得友元技巧变得更加困难了。 - j_random_hacker
购买(buy())是软件的方法,这真的有意义吗?我想卖家接口(由商店和软件生产者[在线销售]实现)似乎更自然。然后,在特定的软件生产者和授权销售商集合之间可以建立访问关系,在现实生活中这是有意义的...再次强调,这主要取决于您想要实现的模型。 - David Rodríguez - dribeas
用户会去找卖家请求购买一个标题。卖家将提供软件,实际上是一组“安装媒体”,以及从软件生产商获得的密钥(该生产商已声明该卖家是可行的)。您可以通过私人友谊来实现访问控制,但最终可能导致更复杂的模型。 - David Rodríguez - dribeas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接