如何初始化作为结构指针的 class 成员

How to Initialize class member that is a pointer to a structure

I have a problem with the application crashing at the line of code where if(!head) is being referenced inside the function: insertNode(). head and tail are class members of type node*. It looks like, I am missing something in the way the class members: head, tail are initialized.. This is the runtime error: "Unhandled exception at 0x00245246 in SLinkedlist_array.exe: 0xC0000005: Access violation reading location 0x00000000."

    slinkedlist.h:
    typedef struct node
    {
        int value;
        struct node* next;
    } node;

    class slinkedlist
    {
    public:
        //ctor, dtor, insertNode(int, int), displayList()
    private:
        node* head, tail;
    };

    slinkedlist.cpp:
    bool slinkedlist::insertNode(int value, int aftNodeVal)
    {
        int toinsertval = value;
        int searchkey = aftNodeVal;
        bool retval = false;

        // If it's a new linked list
        if(!head)  // THIS IS WHERE THE APPLICATION CRASHES!
        {
            node* head = new node;
            head->value = toinsertval;
            head->next = NULL;
            return true;
        }
        else //It's not a new list
        {
            while(head->next != NULL)
            {
                 //some more code here... 
            }
        }
        return retval;
    }

    void slinkedlist::displayList()
    {
        while(!head)
        {
            do
            {
                cout << head->value << " " ;
                head = head->next;
            }
            while(head->next != NULL);
        }
        //return void;
    }

    main.cpp:
    int main()
    {
        slinkedlist *s1 = NULL;
        s1->insertNode(4, -1);
        s1->displayList();
        while(1);
    }`

答案很简单:这里有空指针取消引用:

slinkedlist *s1 = NULL;
s1->insertNode(4, -1);
s1->displayList();

这正是系统告诉你的:"Access violation reading location 0x00000000"

解决方案可以是:

slinkedlist *s1 = new slinkedlist;
s1->insertNode(4, -1);
s1->displayList();
delete s1;

或者像这样(为什么不只使用堆栈上的对象?):

slinkedlist s1;
s1.insertNode(4, -1);
s1.displayList();

或更多 C++ 方式(如果您需要指针):

auto s1 = make_unique<slinkedlist>(); // s1 is a std::unique_ptr<slinkedlist>
s1->insertNode(4, -1);
s1->displayList();
slinkedlist *s1 = NULL;

定义了一个指向 slinkedlist 的指针并确实对其进行了初始化 不幸的是,它将其初始化为 NULL,这是一个安全的停车地址,(通常)不允许任何对象存在。对于绝大多数 CPUs(我曾经工作过的每个 CPU)访问 NULL 附近的死区会使程序崩溃,从而更容易检测错误。

这里不需要指针。如果您不需要指针,请不要使用指针。你的生活会轻松很多。

int main()
{
    slinkedlist s1; // default initializes
    s1.insertNode(4, -1);
    s1.displayList();
    while(1); // rethink this. If you want to hold a program open to see the output 
              // while debugging, place a breakpoint in the debugger.   
}
单独 s1

Default initializing 不会帮助你,因为它会做最少的工作来初始化它的成员变量,而在指针的情况下,最少的工作是什么都不做,保留 headtail 未初始化并指向(有点。 tail 不是指针)到不确定的位置。因为您还没有询问将 NULL 分配给 tail 时应该得到的编译器错误,所以程序显然没有初始化 tail,我假设 slinkedlist构造函数没有做太多。

旁注:如果您有一个不执行任何操作(并且不需要执行任何操作)的构造函数或析构函数,请将它们排除在外,让编译器生成适当的代码。不存在(并且不需要存在)的代码没有错误。

class slinkedlist
{
public:
    //ctor, dtor, insertNode(int, int), displayList()
private:
    node* head, tail; // the * only applies to head.
};

可能是

class slinkedlist
{
public:
    //ctor, dtor, insertNode(int, int), displayList()
private:
    node* head = nullptr;
    node* tail = nullptr;
};

如果您正在编译最新的(2011 或更新的)C++ 标准。您不需要构造函数,默认分配会为您完成工作。您仍然需要一个析构函数以及一个复制构造函数和一个赋值运算符来满足 The Rule of Three.

在旧的 C++ 标准中,您需要使构造函数更智能

class slinkedlist
{
public:
    slinkedlist(): head(NULL), tail(NULL)
    {
    }
    //dtor, insertNode(int, int), displayList()
private:
    node* head; // I recommend splitting the definitions up. It makes the code easier 
                // to read and makes it harder to make mistakes.
    node* tail;
};

您仍然需要析构函数、复制构造函数和赋值运算符。

请注意,这也适用于 node。如果你动态分配一个节点并且没有显式设置 next 到一个值,你将不知道 next 指向哪里,并且所有的测试都像

while(head->next != NULL)

会非常失败。