我对实现双向链表有点困惑,其中列表中的数据是指针。
我的链表类的私有部分如下:
private:
struct node {
node* next;
node* prev;
T* o;
};
node* first; // The pointer to the first node (NULL if none)
node* last; // The pointer to the last node (NULL if none)
unsigned int size_;
正如您所看到的,列表中充满了指向对象的指针,而不仅仅是普通的旧对象,这让我更加困惑。
以下是规范中的描述:
请注意,虽然此列表是在包含的类型T中进行模板化的,但它只插入并删除指向T的指针,而不是T的实例。这可确保Dlist实现知道它拥有插入的对象,它负责如果列表被复制,则复制它们,如果列表被销毁,它必须销毁它们。
这是我当前对insertFront(T * o)的实现:
void Dlist::insertFront(T* o) {
node* insert = new node();
insert->o = new T(*o);
insert->next = first;
insert->prev = last;
first = insert;
}
这似乎错了。如果T没有复制构造函数怎么办?这如何确保列表中对象的唯一所有权?
我可以这么做:
insert->o = o;
这似乎不安全,因为如果你有:
Object* item = new Object();
dlist.insertFront(item);
delete item;
然后该项目也将被销毁。它是否正确?我的理解是在哪里?
感谢阅读。
注意:虽然这看起来像家庭作业,但事实并非如此。我实际上是一个java开发人员只是通过做一个旧学校项目来刷我的指针技能。
答案 0 :(得分:4)
当您有一个指针容器时,您将拥有以下两种使用方案之一:
指向容器的指针,容器在删除包含结构时负责删除指针。
指针被赋予容器但由调用者拥有。调用者负责在不再需要时删除指针。
上面的第1号非常简单。
在数字2的情况下,预计容器的所有者(可能也是调用者)将在删除项目之前从容器中删除该项目。
我故意遗漏了第三个选项,这实际上是您在第一个代码示例中选择的选项。那就是分配一个新项目并复制它。我之所以放弃它是因为调用者可以做到这一点。
将其遗漏的另一个原因是您可能需要一个可以采用非指针类型的容器。始终使用T*
代替T
要求它成为指针可能不如您想要的那么灵活。有些时候你应该强迫它成为一个指针,但我想不出有任何用处(在我的头顶)为容器做这个。
如果您允许用户声明Dlist<MyClass*>
而不是Dlist<MyClass>
,则该列表的所有者会隐式意识到它正在使用指针,这迫使他们假设从上面开始编号为场景2。
无论如何,这里有一些评论的例子:
1。除非您有充分的理由,否则不要分配新的T
项目。这个原因可能只是封装。虽然我上面提到你不应该这样做,但有时你可能想要这样做。如果没有复制构造函数,那么您的类可能是普通旧数据。如果复制非常重要,则应遵循Rule of Three。
void Dlist::insertFront(T* o) {
node* insert = new node();
insert->o = new T(*o); //<-- Follow rule of three
insert->next = first;
insert->prev = last;
first = insert;
}
2。这是您通常会做的事情
insert->o = o;
3。插入后不得删除项目。将所有权传递给您的容器,或者当您和容器都不再需要它时删除该项目。
Object* item = new Object();
dlist.insertFront(item);
delete item; //<-- The item in the list is now invalid