我正在编写一个侵入性链表
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);
};
但是接下来我必须将你的方法转换为方法中的ListNode&#39; next&#39;因为U类可能会意外地隐藏ListAlgorithm使用的ListNode的一些成员。这很容易出错,因为我可能会忘记上传,而编译器也不会对此发出警告。我必须再次将ListNode向下转换为U以获得返回值,但它是安全的,因为&#39; next&#39;采用U的实例u,返回值来自u。
另一项试验是
//U extends ListNode
template<class U>
class ListAlgorhtm {
U& next(ListNode& n);
};
在这种情况下,upcast问题不存在,但是我必须将ListNode向下转换为U作为返回值并且它不安全,因为它不确定n是U的实例。它可能是一个实例另一种扩展ListNode的类型。
在这种情况下,最佳解决方案是什么?我认为这是一个非常基本的设计问题,我想知道我需要学习哪种材料来进行基本的OO设计。
答案 0 :(得分:1)
这里的实际问题是,您允许用户通过子类化向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中使用虚方法,它会增加。
答案 1 :(得分:0)
就你提出的问题而言,无论何时你想对一个类的成员进行操作并避免被派生类隐藏,只要确保你的操作在基础上,所以
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。