C++链表节点是指它自己



我正在创建一个链表,其中包含一副牌。它只有一个头,因为我不需要尾巴。当我添加第一张卡时,一切都很好,当我添加第二张卡时它将自己设置为下一张,并基本上创建了一个无限列表。

我不知道为什么会发生这种事。我已经逐步完成了代码,每当在第二张卡上调用addCard((方法时,头都会在我不做的情况下神奇地变到那张卡上。这听起来很疯狂,但我找不到任何其他解释。非常感谢任何帮助。

标题

#pragma once
#include "Card.h"
class Deck {
public:
bool isEmpty();
~Deck();
void addCard(Card); //at top
Card removeCard(); //from top
void addAllCards(); //All 52 cards
void print(); //print all the cards
private:
int count = 0;
Card* head{ NULL };
};

定义

#include "Deck.h"
#include <iostream>
bool Deck::isEmpty() {
return this->count == 0;
}

void Deck::addCard(Card card) {
if (isEmpty()) {
this->head = &card;
}
else {
card.next = this->head;
this->head = &card;
}
count += 1;
}
Card Deck::removeCard() {
if (!isEmpty()) {
count -= 1;
Card temp = *head;
head = head->next;
return temp;
}
}
void Deck::addAllCards() {
for (int suit = 0; suit < 4; suit++) {
for (int rank = 0; rank < 13; rank++) {
Card newCard = Card(suit, rank);
this->addCard(newCard);
}
}
}
void Deck::print() {
Card* current = head;
while (current != NULL) {
std::cout << current->stringSuitAndRank() << std::endl;
current = current->next;
}
}
Deck::~Deck() {
while (!isEmpty()) {
removeCard();
}
}

驱动

#include <iostream>
#include <string>
#include "Card.h"
#include "Deck.h"
using namespace std;
int main()
{
Card myCard1{ 2, 4 };
Card myCard2{ 4, 5 };
{
Deck myDeck;
myDeck.addCard(myCard1);
myDeck.addCard(myCard2);
cout << endl << "Printing deck..." << endl;
myDeck.print();
myDeck.removeCard();
myDeck.removeCard();
cout << endl << "Printing deck..." << endl;
myDeck.print();
cout << endl << "Adding all cards..." << endl << endl;
myDeck.addAllCards();
cout << endl << "Printing deck..." << endl;
myDeck.print();
}
system("pause");
}

编辑:当我到达列表的末尾时,我在打印方法中也遇到了读取访问冲突。我认为这与nullptr有关,但帮助又被浪费了。

void Deck::addCard(Card card) {

card是该类方法的一个参数。方法参数实际上是方法中的局部对象。这与在此方法中声明一个名为card的对象没有什么不同。唯一的区别是,实际对象是从调用该方法的人那里复制的,但在所有其他方面,它与在该方法中声明的名为card的变量没有什么不同。

就像在这个方法中声明的任何其他变量一样,当它返回这个名为card的对象时,就会被销毁。它会消失的。不会再有了。它将不复存在。它将是一个ex对象。

this->head = &card;

addCard()的这一部分在head成员中存储指向card的指针。到目前为止还不错。但是,不久之后,这个card对象将不复存在。请参见上文。这将成为指向已销毁对象的指针。下一次addCard()被呼叫时,随之而来的是欢乐。

要解决这个问题,你需要重读C++书中的一章,该章解释了C++中的对象是如何工作的,何时创建,何时销毁。如果这个excersize的目标是自己练习实现链接列表,并且您不希望利用C++库的容器,那么您将需要使用newdelete来动态创建对象,因此当这个方法返回时,这些对象将不会消失。他们不会再出现了。它们不会停止存在。它们不会是旧对象。

您的addCard成员函数按值取Card

void Deck::addCard(Card card) {
if (isEmpty()) {
this->head = &card;
}
else {
card.next = this->head;
this->head = &card;
}
count += 1;
}

您正在将局部变量card插入到链表中,当函数终止时,该变量将被销毁。

这个问题的解决方法可以很简单:

void Deck::addCard(Card &card) { /*...*/ }
^ pass reference

然而,我不这么认为,因为调用者也在自动存储中分配这些对象;我们只需要传递对调用方本地对象的引用,而不是动态分配的节点。

将本地对象放入链接列表是合法的,但在它们"超出范围"之前,必须确保它们已从列表中删除。可能出现以下情况:

{
list_class li;
node_class n1, n2, n3;
li.add_by_reference(n1, n2, n3);
// OK? maybe.
// here n1, n2, n3 destructors get called then li.
// if li doesn't touch the nodes, everything is cool
}

但这是不正确的:

{
list_class li;
for (int i = 0; i < 5; i++) {
node_class n;
li.add_by_reference(n);
}
// A new n object is created and destroyed for each loop iteration.
// The list still has links to a destroyed node, and a new one is
// being added to it.
}

您的程序(特别是Deck::addAllCards(中的情况类似于这里的第二个示例。第二个例子的修复方法是动态分配:

li.add_by_reference(*new node_class);

不过,现在我们出现了内存泄漏;CCD_ 16的析构函数可能不知道节点是动态分配的并且不释放它们。

假设我们在函数终止之前放入代码来释放节点,比如:

while (!li.empty()) {
node_class *pn = li.dequeue_head();
delete pn;
}

不过,异常安全存在问题;如果new抛出,则函数将被放弃而不执行此循环;则只调用CCD_ 18的析构函数。

相关内容

  • 没有找到相关文章

最新更新