C++类设计问题

3

我有一个类Node。这个类可以添加或删除与自己相关的其他节点。Node被List类使用。为了防止在使用过程中(外部,即不是通过适当的类)直接修改节点,并导致List类出现问题,节点的添加/删除函数要么是protected或private。这需要List类是Node的友元。

然而,这样做的问题在于List类本身是其他子类的模板类,为每个子类添加原型/添加friend关键字显然不是最佳解决方案。

如何设计Node和List类/子类,以便:

  • Node 不能被外部构造,只能由特定的类/子类构造?
  • Node 可以根据上述条件构造/删除其他节点?
  • 仅特定类(List、list子类和list辅助类 - list辅助类不是list的子类)可以访问Node函数?
  • 给定以上条款,节点变量(Item)是公开可访问的吗?
  • List、list子类和list辅助类可以直接或间接修改Node的非公共变量吗?

这些是否可能,并且如果可能,应该怎么做呢?


为什么外部代码会有访问 list 中的 node 的权限呢?node 是列表实现的内部细节,不应该泄漏到列表之外。对于列表迭代,您可以提供一个 iterator 类型,它具有访问权(但受您控制),只会提供访问和递增操作...与STL列表非常相似...再次,请查看那里的代码。 - David Rodríguez - dribeas
4个回答

6
标准库的std::list类模板是一个设计链表的示例,使得客户端代码无法访问内部节点等。

没有访问权限,就不会有不必要的干扰......

然而,一般来说,更实际的做法是稍微信任一下,不需要自己承担确保别人的代码正确性的责任。在C++中表达所有的使用限制非常繁琐。稍微开放一点可以节省很多工作-这是一种权衡。

祝好!


3
+1 是对客户的信任。因为这是 C++,而这就是我们的一贯做法。 - Seth Carnegie
我更关注安全而不是无知。 - SE Does Not Like Dissent
@SSight3:你在评论中提到的“security”具体是什么?如果你指的是入侵保护方面的“security”,那么你可能需要重新考虑,因为“private”或“protected”并不能阻止有权访问代码的用户做任何事情... - David Rodríguez - dribeas
@David:子类将templatelist澄清(并在适用的情况下)扩展为其他特定角色,例如charlist(因此它可以读取以NULL结尾的字符串,处理std :: string,附加null等)。助手类基本上是templatelist的迭代器(包括const应用程序的只读迭代器)。Templatelist有自己的迭代器,但在const调用期间无法使用。 - SE Does Not Like Dissent
@SSight3:所以你有一个列表和一个字符列表,它基本上是一个列表但以空字符结尾,它们是否要多态使用?你想将charlist传递给接受list的函数,并期望它像常规列表一样工作吗?可以通过向量(管理数据的底层类型)来实现字符串,但这并不意味着字符串是向量。对于处理list的每个操作,是否都可以自由地应用于charlist - David Rodríguez - dribeas
显示剩余2条评论

2
我会把 Node 定义为 List 的受保护嵌套类:
class List
{
    ...
    protected:
        class Node
        {
            ...
        };
};

这样,只有List及其子类才能访问它。由于它嵌套在List中,List可以访问它的私有/受保护成员和函数。 它还有助于突出两个类之间的功能关系。这可能已经解决了您所有的要点,除了第三个。 编辑 仔细检查事实后,似乎在C++中,封闭类并没有特殊的访问权限来访问嵌套类成员(这似乎是Java的事情),请参见here。因此,您需要将Node成员设置为public,但我仍然认为这种解决方案鼓励良好的封装。

我曾经考虑过这个问题,但是由于列表使用了辅助类,所以我不得不将其搁置。我可能需要重新编写辅助类,使其成为列表本身的子类。但这样做会使节点通过List::Node可访问吗? - SE Does Not Like Dissent
@SSight3:这将使Node仅对朋友和亲戚(即friend类和派生类型)可通过List::Node访问。我不确定您如何管理辅助类,或者它们的作用是什么(即您在问题中没有提供足够的信息),但您可能可以继续执行相同的操作,或将它们声明为友元(我会避免无谓地强制继承)。 - David Rodríguez - dribeas

1

我不确定我是否理解了问题,所以我将提供不同的方法而不是答案:

  • list成为node唯一的朋友,并确保它作为受保护的方法提供所有从list派生类型所需的操作。在这种方法中,list类型作为所有派生类型的node类的代理。

  • node成为list的受保护内部类型。它是list内部类型并且是受保护的,这使得list层次结构之外的所有人都无法使用该类型,node中的所有方法都可以是公共的。

  • 友好和信任:保持设计不变,并使node中的某些操作对所有人公开。信任用户的内容并记录您的list类构建的不变量,以便用户代码知道不要执行哪些操作。

我更喜欢第一种选择,因为它提供了一个具有管理节点责任的列表,这是一件好事,至少比共享责任要好...只有一个地方可能出错:列表类和它自己维护不变量。

0
作为一个想法,可以使用Mac的答案:
声明一个名为NodeAccess的类,NodeAccess包含在protected下声明的Node类(就像Mac的答案一样)。
将node中的函数声明为public。
class NodeAccess
{
    protected:
        class Node
        {
            public:
            void Function(){}
        };
};

然后,对于想要访问该节点的每个类,它们都以受保护的方式继承NodeAccess,这将授予它们和所有其他子类在受保护规则集内访问节点的权限,但防止任何其他类直接访问节点。

class Helper: protected NodeAccess
{

};

class OtherHelper: protected Helper
{

};

//等等


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