我有一个C ++代码:
+: contract violation
expected: number?
given: '()
argument position: 2nd
other arguments...:
所以我的问题是我如何实现一个复制构造函数(假设在对象lb上),它创建了list参数的深层副本,还添加了用于在空列表和非空列表上测试复制构造函数的代码? 提前谢谢。
答案 0 :(得分:3)
编程的一个重要规则是不要重复自己(DRY)。如果您有一个添加的功能,并且您知道它可以正常工作,请继续将其用于与添加相关的作业。这意味着保持添加死愚蠢和多才多艺符合您的最佳利益。
应用DRY原则,复制构造函数(假设AddTail
方法正常工作)非常简单:为源列表中的每个节点调用AddTail
。
LinkedList::LinkedList(const LinkedList & src):Head(nullptr)
{
NodePtr node = src.Head;
while (node != nullptr)
{
AddTail(node->Item);
node = node->Next;
}
}
由于Copy and Swap Idiom,有一个工作副本构造函数使得赋值运算符也很简单:
LinkedList & LinkedList::operator=(LinkedList src)
// pass by reference performs the copy
{
std::swap(Head, src.Head); // now just swap the head of the copy
// for the head of the source
return *this;
} // destructor fires for src and cleans up all the nodes that were on this list
要完成The Rule of Three三连胜,我们需要一个析构函数。这像
复制构造函数是DRY的应用程序:反复调用节点删除功能,直到列表为空。删除功能几乎是任何链表的必然要求,所以我在这里假设有一个名为Remove
。
LinkedList::~LinkedList()
{
while (Head != nullptr)
{
NodePtr temp = Head;
Remove(Head);
delete temp;
}
}
所以现在基于链接列表无法运行的两个函数,我们已经实现了基本维护所需的所有其他功能。您所需要的只是经过测试且没有错误的Add
和Remove
功能,其余部分几乎是免费的。
因为AddTail
函数击中了我的一个宠物洞......这是一个大大降低函数复杂性的技巧:
void LinkedList::AddTail(int Item)
{
NodePtr *Crnt = &Head; // instead of pointing where Head points, point at
// Head now we don't care if it is head or any
// other node's Next. They are all abstracted to
// the same thing: A pointer to where the next
// node can be found
while (*Crnt != NULL) // keep looking until end of list
{
Crnt = &(*Crnt)->Next; // by pointing at the next Next pointer
}
//Add item to the tail of the linked list
NodePtr node = new Node;
node->Item = Item;
node->Next = NULL;
*Crnt = node; // Now just plop the new node over top of the terminating NULL
}
Remove
函数,我没有实现,使用相同的指针到指针技巧。
答案 1 :(得分:2)
试试这个(https://ideone.com/9lywXc使用原始发布的代码)
LinkedList::LinkedList(const LinkedList& other):Head(nullptr)
{
cout << "copy constructor called:\n";
if(other.Head == nullptr) return;
NodePtr dummyHead = new Node;
NodePtr curr = dummyHead;
NodePtr othcurr = other.Head;
for(; othcurr!=nullptr; othcurr = othcurr->Next)
{
curr->Next = new Node;
curr = curr->Next;
cout << (curr->Item = othcurr->Item) << ",";
curr->Next = nullptr;
}
Head = dummyHead->Next;
delete dummyHead;
}
int main()
{
LinkedList la;
la.AddTail(30);
la.AddTail(60);
la.AddTail(90);
LinkedList lb(la);
return 0;
}
输出:
复制构造函数:
30,60,90,
答案 2 :(得分:2)
这是对user4581301之前的答案的改进,因此复制构造函数在输入列表大小上是O(n)。在封装列表类中使用一个额外的指针跟踪尾部:
class LinkedList
{
public:
LinkedList():Head(nullptr),Tail(nullptr){}
LinkedList(const LinkedList& other);
~LinkedList() = default; // destructor
void AddTail(int); // adds item to tail
private:
NodePtr Head;
//KEEP TRACK OF TAIL POINTER WITH EXTRA MEMBER
NodePtr Tail;
};
//via user4581301 response
LinkedList::LinkedList(const LinkedList & src):Head(nullptr),Tail(nullptr)
{
NodePtr node = src.Head;
while (node != nullptr)
{
AddTail(node->Item);
node = node->Next;
}
}
//Adding on tail
void LinkedList::AddTail(int Item)
{
NodePtr np = new Node{Item,nullptr};
if(Tail == nullptr && Head == nullptr)
Head = Tail = np;
else
{
Tail->Next = np;
Tail = Tail->Next;
}
}
答案 3 :(得分:2)
关于测试: 您可以添加功能来吐出列表的内容。或者您可以将其子类化为测试扩展。或者你可以使用运算符&lt;&lt;():
打破这样的封装class LinkedList
{
public:
LinkedList():Head(nullptr),Tail(nullptr){}
LinkedList(const LinkedList& other);
~LinkedList() = default; // destructor
void AddTail(int); // adds item to tail
//breaks encapsulation but make some nice sugar to look inside
friend ostream& operator<<(ostream& s, LinkedList& l)
{
s << "list contents: ";
NodePtr c = l.Head;
for(; c!=nullptr;c=c->Next)
s << c->Item << " ";
s << endl;
return s;
}
private:
NodePtr Head;
NodePtr Tail;
};
这样
int main()
{
LinkedList la;
la.AddTail(30);
la.AddTail(60);
la.AddTail(90);
LinkedList lb(la);
cout << lb;
return 0;
}
吐出: 列表内容:30 60 90