我正在创建一个自定义链接列表类来存储我为作业创建的程序中的字符串。我们得到了一个链接列表讲义,适用于整数,并被告知重新调整它以进行字符串存储,但是在尝试运行它时遇到错误。
我在抛出'std :: logic_error'的实例后得到错误“”终止调用 what():basic_string :: _ S_construct null无效“”(我在周围搜索并发现它是因为字符串被设置为null,但我不知道如何修复错误,我猜它是用行8但是我玩弄它并没有成功。)我四处搜索并查看了类似的问题,却找不到任何有用的东西。
#include <cstdlib>
#include <iostream>
#include <string>
#include <cstdio>
#include <iomanip>
using namespace std;
struct node {
node(string current) { data=current; next=NULL; }
string data;
node *next;
};
class list {
public:
list(int N=0, string current);
~list();
bool empty() const { return N == 0; }
void clear();
void insert(int, const string &);
void push_front(const string ¤t);
friend ostream & operator<<(ostream &out, const list ¤t);
private:
int N;
node *head;
node *findnode(int);
};
list::list(int M, string current) {
N = M;
head = new node;
for (int i=0; i<N; i++)
insert(0, current);
}
list::~list() {
clear();
delete head;
}
void list::clear() {
while (!empty()) remove(0);
}
void list::insert(int i, const string &din) {
node *p = new node(din);
node *pp = findnode(i-1);
p->next = pp->next;
pp->next = p;
N++;
}
inline
node *list::findnode(int i) {
if (i == -1)
return head;
node *p = head->next;
while (i--)
p = p->next;
return p;
}
void list::push_front(const string ¤t) {
head = new node;
head->next;
}
ostream& operator<<(ostream& out, const list& current)
{
out << current;
return out;
}
const string rank[] = { "Ace", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "Jack", "Queen", "King" };
const string suit[] = { "Clubs", "Diamonds", "Hearts", "Spades" };
string random_card(bool verbose=false) {
string card;
card = rank[ rand()%13 ];
card += " of ";
card += suit[ rand()%4 ];
if (verbose)
cout << card << "\n";
return card;
}
int main(int argc, char *argv[])
{
bool verbose = false;
int seedvalue = 0;
string stop_card = "Queen of Hearts";
for (int i=1; i<argc; i++) {
string option = argv[i];
if (option.compare(0,6,"-seed=") == 0) {
seedvalue = atoi(&argv[i][6]);
} else if (option.compare(0,6,"-stop=") == 0) {
stop_card = &argv[i][6];
} else if (option.compare("-verbose") == 0) {
verbose = true;
} else
cout << "option " << argv[i] << " ignored\n";
}
srand(seedvalue);
list deck[4];
while (1) {
string card = random_card(verbose);
char first[10];
char second[10];
sscanf(card.c_str(), "%s of %s", first,second);
// reverse engineer card suit and rank
int index2;
//suit index
for(int i=0; i<4; i++){
if(suit[i]==second){
index2=i;
break;
}
}
deck[index2].push_front(first);
if (card.compare(stop_card)==0){
break;
}
}
// print formatted table contents to stdout
cout << "Clubs : ";
cout << setw(3) << deck[0];
cout << endl;
cout << "Diamonds : ";
cout << setw(3) << deck[1];
cout << endl;
cout << "Hearts : ";
cout << setw(3) << deck[2];
cout << endl;
cout << "Spades : ";
cout << setw(3) << deck[3];
cout << endl;
}
答案 0 :(得分:2)
以下是阻碍构建(读取:编译时错误)或实际运行时的重大问题。这并没有声称这些所有的错误,但它当然值得考虑。我应该在顶部注意到&#34; sentinel&#34;的概念。在链表管理中,头节点分配几乎 - 从不,并且此代码不是例外之一。如果列表是&#34;空&#34; head
应为null。如果它不为空,则head
不应为空。它就是这么简单,如果遵循这个代码,这个代码就会越来越简单。
有了这个,请继续阅读。
无效代码:
list(int N=0, string current);
原因:C ++要求所有参数跟随第一个提供默认值的参数也具有默认值。如果N是第二个参数,或者current
也被赋予默认值(当然,如果 都没有默认值),这将是有效的。以下所有内容均有效:
list(int N, string current);
list(int N, string current = "");
list(int N=0, string current = "");
如编写,它将无法编译。
无效的代码:没有匹配的构造函数
head = new node;
原因:结构node
未定义默认符合构造函数(一个没有参数,或者所有参数都带有默认值)但 指定非默认值构造函数(需要至少一个参数的构造函数)。因此,语言提供的默认构造函数不自动生成,并且找不到node::node()
构造函数。
代码不正确:表达式结果未使用
void list::push_front(const string ¤t) {
head = new node;
head->next; // THIS LINE
}
原因:此代码盲目地覆盖head
指针中当前占用的任何内容,其中包含新的(无效,请参阅上面的原因)节点分配。 head
之前的任何内容都会永久泄露,current
未被使用。通过分配一个current
作为值的新节点来解决此问题,将其next
指针设置为head
和head
到新节点:
void list::push_front(const string ¤t)
{
node *p = new node(current);
p->next = head;
head = p;
}
无限递归
ostream& operator<<(ostream& out, const list& current)
{
out << current;
return out;
}
原因:此代码实际上会调用自身。递归。永远(好吧,直到你用完了调用堆栈空间)。
NULL指针解除引用
inline node *list::findnode(int i)
{
if (i == -1)
return head;
node *p = head->next;
while (i--)
p = p->next;
return p;
}
原因:这将通过i
迭代的有效性检查不受限制地执行列表。现在想象一下它在空列表中的作用(在您的情况下,这意味着head
非空,但head->next
null)在传递任何时< / em> -1
:它将为i=0
返回NULL,并且对于其他所有内容都是完全未定义的行为。
NULL指针解除引用
void list::insert(int i, const string &din)
{
node *p = new node(din);
node *pp = findnode(i-1);
p->next = pp->next;
pp->next = p;
N++;
}
这假设pp
永远不会在返回时为空,正如我们已经与之前的项目讨论的那样,它肯定可以成为{{1 }}是列表中的唯一节点,因此是#34;空的&#34;。在使用它进行解除引用之前,这不会尝试检查head
是否为NULL。这种小孩手套处理和必须考虑的例外与维持一个哨兵&#34;直接相关。头节点。解决这个问题最简单的方法是(a)不要使用哨兵节点;使用通用标记值 pp
,然后(b)在使用之前检查您的返回值。
歧义参考:排名
nullptr
原因:标准库定义了一个名为std::rank
的特殊结构,用于确定多维数组中的维数。如果代码顶部有card = rank[ rand()%13 ];
,编译器现在必须选择哪一个(命名空间using namespace std;
中的那个或您在此代码之前定义的数组),以及它不能这么明确地这样做。因此它不会编译。注意:这是通过隐式包含<type_traits>
引入的,std
可能包括<string>
,<iostream>
,<iomanip>
或其他任何嵌套包含。您可以通过多种方式解决此问题,包括(但不限于)广告素材using
子句,使用本地功能包装器将rank
数组重命名为不会发生冲突的内容函数等中的静态rank
。
从已签名类型到无符号类型的隐式转换(次要)
srand(seedvalue);
原因:std::srand()
需要unsigned int
个参数;你传递了有符号整数。可以static-cast
到unsigned int
,也可以将seedValue
的类型更改为unsigned int
。
无效代码
list deck[4];
原因:类list
没有默认构造函数。回想一下此回复中的第一项。如果你解决了这个问题,你也可以解决这个问题。
我还没有运行代码。我会强烈建议处理这些问题,并认真考虑不使用&#34; sentinel&#34;列表头的节点。链接列表代码实际上会在您知道&#34; null head
表示列表为空,非空head
表示它不是。
我没有声称这是所有错误。这些只是我在查看代码时看到的,除了其中一个之外的所有代码都是重要的。
编辑示例运算符重载
注意:如果您修复链接列表以在列表为空(建议)时使用null作为头值,则需要将其更改为仅从head
而不是head>next
开始。
std::ostream& operator <<(std::ostream& os, const list& lst)
{
const node *p = lst.head ? lst.head->next : nullptr;
while (p)
{
os << p->data;
if ((p = p->next)) // note: assignment intentional
os << ',';
}
return os;
}