c++ - 将对象指向 NULL 会将成员值置于什么位置?

标签 c++ class oop null

抱歉这个冗长的标题。

class Student
{
public:
// Some routines

private:
string name;
int grade;
}

主要是:

Student* newStudent = NULL;

我知道NULL等同于零,但是现在name和grade是怎么设置的呢?当我这样做时:

cout << newStudent->getName();

它不返回 0 - 相反,Visual Studio 给了我某种错误。

所以我也想知道,newStudent 到底指的是什么?它只是消除了所有私有(private)数据,并将整个类指向 0 吗?

编辑:

好的,克里斯 - 我就是这么想的。我的后续问题是: 我如何在 getName 函数中检查这一点。

我不能像这样使用 bool,

if (name == 0)
// Return an error message.

那么有解决办法吗?

编辑: VS错误是: Ex8_3.exe 中 0x00FFAAA6 处的未处理异常:0xC0000005:访问冲突读取位置 0x00000014。

请注意,这实际上是一个更大程序的一部分,我只是隔离了问题部分。

编辑: 好的,这是导致问题的代码。我将只发布整个程序,因为它不是太大,而且我可以使用任何我能得到的建设性批评。

#include <iostream>
#include <string>
#include <vector>
using namespace std;

class Student
{
public:
    // Accessor
    string getName();
    vector<string> getCourseList();
    Student* getLink();

    // Mutators
    void setName (string aName);        
    void setCourses(vector<string> list);       
    void setLink (Student* aLink);

private:
    string name;
    vector<string> courseList;      
    Student* link;

};

// Linked-list Non-Member Functions.
typedef Student* NodePtr;
void head_insert (NodePtr& head); 
NodePtr search (NodePtr head, string target); 
NodePtr searchPrevious (NodePtr head, string target);
void deleteNode (NodePtr& head, string target);
void show_list (NodePtr& head);
void showFirstCourses (NodePtr& head);

int main()
{
    NodePtr headPointer = NULL;
    head_insert(headPointer);
    headPointer->setName("Tim");

    head_insert(headPointer);
    headPointer->setName("Jan");

    head_insert(headPointer);
    headPointer->setName("Jim");

    deleteNode(headPointer, "Tim");

    NodePtr test1 = NULL;
    head_insert(test1);     // This is where the new Student is created.

    test1 = search (headPointer, "Jon");    // Still experiencing a problem between here and the next instruction.

    cout << test1->getName();   // Something is wrong if this == NULL.


    system("pause");
    return 0;
}

string Student::getName()
{
    return name;
}

vector<string> Student::getCourseList()
{
    return courseList;
}

Student* Student::getLink()
{
    return link;
}

void Student::setName (string aName)
{
    name = aName;
}

void Student::setCourses(vector<string> list)
{
    courseList = list;
}

void Student::setLink (Student* aLink)
{
    link = aLink;
}

void head_insert (NodePtr& head)
{
    NodePtr temp_ptr; 
    temp_ptr = new Student; 
    temp_ptr->setLink(head);    // Link this to what the head is linking to.
    head = temp_ptr;    // Use this node as the new head.
}

NodePtr search (NodePtr head, string target)        // Needs to be tested, to see if getLink is working correctly.
{
    // Point to the head node 
    NodePtr here = head; 

    // If the list is empty nothing to search 
    if (here == NULL) 
    { 
        return NULL; 
    } 
    // Search for the item 
    else 
    { 
        // while you have still items and you haven't found the target yet 
        while (here->getName() != target && here->getLink() != NULL) 
            here = here->getLink(); 
         // Found the target, return the pointer at that location 
         if (here->getName() == target) 
                return here; 
         // Search unsuccessful, return Null 
         else 
                return NULL; 
    } 
}

NodePtr searchPrevious (NodePtr head, string target)    // This function needs to be checked for the same things the above function does.
{
    // Point to the head node 
    NodePtr here = head; 

    // If the list is empty nothing to search 
    if (here == NULL) 
    { 
        return NULL; 
    } 
    // Search for the item 
    else 
    { 
        // while you still have items and you haven't found the target yet 
        while (here->getName() !=target && (here->getLink())->getName() != target && (here->getLink())->getLink() != NULL)
            here = here->getLink(); 
         // Found the target, return the pointer at that location 
        if (((here->getLink())->getName() == target) || (here->getName() == target))
        {
            return here;
        }
         // Search unsuccessful, return Null 
         else 
                return NULL; 
    } 
}

void deleteNode (NodePtr& head, string target)      
{
    NodePtr toBeDeleted = search (head, target);
    NodePtr previousPointer = searchPrevious (head, target);

    // Make sure the desired value is in the list.
    if (toBeDeleted ==  NULL)
    {
        cout << target << " is not in the list, so it can't be deleted.\n";
        return;
    }

    // Test to see if we are removing the first node.
    if (previousPointer == toBeDeleted)
    {
        head = toBeDeleted->getLink();
        delete toBeDeleted;
        return;
    }

    // Connect the node from before to after.
    previousPointer->setLink(toBeDeleted->getLink());

    delete toBeDeleted;
}

最佳答案

不要将指向 NULL 的指针上的 namegrade 视为设置为某物。当 Student * 指针设置为 NULL 时,它什么也没有指向。就好像你指着房间里的一 block 空地,问我你指的那个人叫什么名字。

你不能解引用一个 NULL 指针,所以这样做:

Student *s = NULL;
s->name;

是未定义的行为。什么事情都可能发生;最有可能的是,你会遇到段错误。 (但这是一个实现细节,并不能保证。)

可以检查空值,只需:

if(s) {
    // s isn't NULL.
}

(您也可以使用 if(s != NULL)if(s != nullptr)。)

现在我们已经有了您的代码,让我们看看这个案例:

test1 = search (headPointer, "Jon");    // Still experiencing a problem between here and the next instruction.

cout << test1->getName();   // Something is wrong if this == NULL.

这里的问题是第二行:在搜索失败的情况下,你为什么期望能够输出学生的姓名?我们还没有找到学生!我们需要解决没有学生的问题:

test1 = search(headPointer, "Jon");
if(test1) {
    cout << "Found student; his name is " << test1->getName() << endl;
} else {
    cout << "Couldn't find that student." << endl;
}

同样,我们这里有一些错误的逻辑:

while (here->getName() != target && here->getLink() != NULL) 
    here = here->getLink(); 

假设 here->getLink() 在我们到达此列表末尾的不可避免的情况下返回 NULL。 while (here->getName()) 中的条件现在解除对无效 (NULL) 指针的引用。让我们显着重构整个函数:

NodePtr search (NodePtr head, string target) {
    // Where in the list we are:
    NodePtr here = head;
    // Check that head points at a student:
    while(here != NULL) {
        // Check if this is the student we are searching for:
        if(here->getName() == target) {
            // It is. Great! Return him/her:
            return head;
        }
    }
    return NULL;
}

请注意,除非您需要实现链表,否则 STL(标准模板库)有几个列表容器:std::liststd::vector 是两个包含对象列表的容器。它们具有不同的性能特征,您需要在这些特征之间做出选择,但它们比重新发明一个链表更容易使用。

最后一件事:

OK I can take that solution, I was just thinking there might be a more elegant solution - like including this check in the getName function if possible. The reason being, is that I would have to include the check you described above every time I call getName.

让我们谈谈getName如果我们在该函数中允许一个 NULL 学生,我们该怎么办?因为我们无法得到我们没有的学生的名字……很难说。我们会输出错误吗?引发异常?杀死程序?

在成员函数中,期望您正在操作的对象存在通常是公平的游戏。如果不是,则说明调用方在 NULL 指针上执行 ->getName() 时存在错误。同样,在普通函数中,我们可以取一个引用:

void do_something_with_a_student(Student &s)

引用不应该为 NULL,因此允许此函数假定它始终有一个有效的学生。您唯一需要担心的地方是“我有一个有效的学生吗?”是逻辑上应该引起关注的地方,例如刚刚搜索过学生。如果我在类(class)花名册中搜索“Billy”,我可能有也可能没有这样的学生:在这里,我们需要“不存在”的值; NULL 对此很有用。

即使您采用指针,您也始终可以记录(可能带有注释)该函数仅允许非 NULL 指针。这在缺少引用的 C 中更常见;在 C++ 中,引用是更好的选择。

关于c++ - 将对象指向 NULL 会将成员值置于什么位置?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22586323/

相关文章:

java - 为什么有些类在创建实例时不需要单词 "New"?

c# - 参数过多的构造函数

java - 字段被声明为私有(private),但我可以直接访问

java - C++:按值传递对象的原因

c++ - 在类中不定义静态数组大小是不是很糟糕?--而是让它自动调整大小

C++ 与 asio 的双工套接字通信

c++ - Boost Spirit 罗马数字解析器示例

c++ - 复制构造函数错误 : returning a value from a constructor

c++ - 为什么静态成员初始化需要是 "typed again"?

c++ - 我如何在我的进程空间内 "mount"而不是 root?