侵入式数据结构的OO设计

OO design for intrusive data structure

本文关键字:OO 设计 数据结构      更新时间:2023-10-16

我正在编写一个侵入性链表

class ListAlgorithm {
    ListNode& next(ListNode& n) {
        //returns an object of type ListNode linked to n.
    }
};

用户通常希望在ListNode上添加一些功能(例如一些附加数据),如下所示:

class UserNode : public ListNode {
    void operationOnUserData();
    int userData;
};

然后,用户必须将"next"返回的ListNode向下投射到UserNode中。这很不方便。因此,我试图使ListAlgorithm成为一个模板类:

//U extends ListNode
template<class U>
class ListAlgorihtm {
    U& next(U& u);
};

但是我必须将u升级到方法"next"中的ListNode中,因为类U可能会意外隐藏ListAlgorithm使用的一些ListNode成员。这很容易出错,因为我可能会忘记向上转换,编译器不会对此发出警告。对于返回值,我必须再次将 ListNode 向下投射到 U 中,但它是安全的,因为"next"需要您的实例 u,并且返回值来自 u。

另一项试验是

//U extends ListNode
template<class U>
class ListAlgorhtm {
    U& next(ListNode& n);
};

在这种情况下,上播问题不存在,但我必须将 ListNode 向下投射到 U 中以获取返回值,这是不安全的,因为它不确定 n 是 U 的实例。它可能是扩展ListNode的另一种类型的实例。

在这种情况下,最好的解决方案是什么?我认为这是一个非常基本的设计问题,我想知道我必须学习什么样的材料才能进行这样的基本OO设计。

您的实际问题是,您允许用户通过子类向ListNode对象添加任意数据和操作来子类化ListNode并弄乱其语义。因此,这使得用户有必要将实际ListNode方法的ListNode&返回值解释为这些返回值在语义上不是的东西。

这个语义性质的问题反映在你的代码突然变得多么乏味上,一个不相关的类(ListAlgorithm)的强制转换和模板化是由于你的问题"传播"和感染代码的其他部分。

这是一个解决方案:不应允许ListNode对象也是UserNode对象。但是,应该允许它携带一个可以检索和操作的UserData对象。

换句话说,您的列表成为一个简单的容器模板,如 std::list ,用户可以指定他们需要的操作和数据成员,作为他们用作模板参数的类定义的一部分。

class IListNode
{
    public:
        // whatever public methods you want here
    protected:
        // pure virtual methods maybe?
};
class ListNode : public IListNode
{
    // List node class, no data
};
template<class UserDataType>
class ListNodeWithData : public IListNode
{
    private:
        UserDataType data;
    public:
        ListNodeWithData <UserDataType>(UserDataType &data) :
            data(data)
        { }
        const UserDataType& getData() {
            return data;
        }
};
class ListAlgorithm
{
    public:
        template<class UserDataType>
        ListNodeWithData<UserDataType>& next(const ListNodeWithData<UserDataType>& node) {
            // Do stuff
        }
        ListNode& next(const ListNode& node) {
            // Do stuff, which may be very similar to the stuff done above
            // in which case you may want to prefer to just define the
            // method below, and remove this one and the one above:
        }
        // You should define either this method or the two above, but having
        // them all is possible too, if you find a use for it
        IListNode& next(const IListNode& node) {
            // Do generic stuff
        }
};

就结果类的大小而言,我只知道如果你在IListNode中使用虚拟方法,它会增加。

就您提出的问题而言,任何时候您想对类的成员进行操作并避免被派生类隐藏,只需确保您的操作在基础上,所以

template<class U>
class ListAlgorihtm {
public:
  U& next(U& u) {
    return static_cast<U&>(return nextNode(u));
  }
private:
  ListNode& nextNode(ListNode& n);
};

也就是说,对于此问题集,您有很多选择。 Boost 库有一个"侵入式"库,它将节点信息嵌入为base_hook(作为用户数据的基础)或member_hook(作为类的成员,这避免了您描述的一些问题)。 在 http://www.boost.org/doc/libs/1_57_0/doc/html/intrusive.html 查看。