0

我正在创建一个自定义链表类来存储我为分配创建的程序中的字符串。我们得到了一个适用于整数的链表讲义,并被告知要对其进行重新工具以进行字符串存储,但是在尝试运行它时遇到了错误。

我收到错误“”在抛出 'std::logic_error' what() 的实例后调用终止:basic_string::_S_construct null not valid""(我四处搜索发现这是因为一个字符串被设置为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 &current);

      friend ostream & operator<<(ostream &out, const list &current);

    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 &current) {
      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;

    }
4

1 回答 1

3

以下是阻碍构建(阅读:编译时错误)或实际运行时的重大问题。这并没有声称这些都是错误,但它当然值得考虑。我应该立即指出,在链表管理中几乎不需要“哨兵”头节点分配的概念,这段代码也不例外。如果列表为“空” head,则应为空。如果它不为空,head不应为空。就是这么简单,如果遵循这个代码,代码会更加简单。

有了这个,请继续阅读。


无效的代码:

list(int N=0, string current);

原因:C++ 要求提供默认值的第一个参数之后的所有参数也具有默认值。current如果 N 是第二个参数,或者如果也被赋予了默认值(或者当然,如果两者都没有默认值) ,这将是有效的。以下所有内容均有效:

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 &current) {
    head = new node;
    head->next; // THIS LINE
}

head原因:这段代码盲目地用新的(无效的,原因见上文)节点分配覆盖了指针中当前占用的任何内容。之前的任何东西都会head永远泄露,并且current无论如何都不会被使用。通过分配一个新节点current作为值来解决这个问题,将其指针设置为next新节点:headhead

void list::push_front(const string &current) 
{
    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 空的)当传递任何东西时 -1它将返回 NULLi=0并且对于其他所有东西都是完全未定义的行为。


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永远不会为空,并且正如我们已经讨论过的前一个项目,它最肯定可以是何时head是您列表中的唯一节点,因此是“空的”。这不会在pp将其用于取消引用之前尝试检查 NULL。这种儿童手套处理和必须考虑的异常与维护“哨兵”头节点直接相关。修复它的最简单方法是 (a) 不要使用哨兵节点;使用通用哨兵 nullptr,并且(b)在使用它们之前检查您的返回值。


模棱两可的参考:rank

card = rank[ rand()%13 ];

原因:标准库定义了一个特殊的结构体,称为std::rank用于确定多维数组中的维数。使用using namespace std;代码顶部的 ,编译器现在被迫选择哪一个(命名空间中std的那个或您在此代码之前定义的数组),并且它不能明确地这样做。因此它不会编译。注意:这是由隐式包含引入的,<type_traits>它可能包含在<string>、或许多其他嵌套包含中的任何一个中。您可以通过多种方式解决它,包括(但不限于)创意子句、将数组重命名为不冲突的内容、在函数中使用围绕局部静态的函数包装器等。<iostream><iomanip>usingrankrank


从有符号到无符号类型的隐式转换(次要)

srand(seedvalue);

原因:std::srand()unsigned int参数;您正在传递一个有符号整数。要么static-casttounsigned int要么改变 to 的seedValue类型unsigned int


无效的代码

list deck[4];

原因:类list没有默认构造函数。回想一下此响应中的第一项。如果你解决了这个问题,你也将解决这个问题。


我什至还没有运行代码。我强烈建议您解决这些问题,并认真考虑不要为您的列表头使用“哨兵”节点。一旦您“知道”空值表示列表为空,链表代码实际上会自行编写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;
}
于 2013-09-24T02:32:17.327 回答