将对象指向NULL会将成员值置于何处?

时间:2014-03-23 02:27:51

标签: c++ class oop null

对罗嗦的标题感到抱歉。

class Student
{
public:
// Some routines

private:
string name;
int grade;
}

主要:

Student* newStudent = NULL;

我知道NULL与零相同,但现在名称和等级是什么?当我这样做时:

cout << newStudent->getName();

它不会返回0 - 相反,Visual Studio会给我一些错误。

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

编辑:

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

我不能像布尔一样使用

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;
}

3 个答案:

答案 0 :(得分:3)

不要将指针上的namegrade视为设置为某事的NULL。当Student *指针设置为NULL时,它指向任何内容。就好像你指着一个房间里空荡荡的空间,问我你所指的那个人的名字是什么。

你不能取消引用NULL指针,所以这样做:

Student *s = NULL;
s->name;

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

可以检查null,只需使用:

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。 whilehere->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是包含对象列表的两个容器。它们具有不同的性能特征,您需要在这些特性之间进行选择,但它们比重新发明链表要容易得多。

最后一件事:

  

好的我可以采用这个解决方案,我只是觉得可能有更优雅的解决方案 - 比如在可能的情况下将此检查包括在getName函数中。原因是,每当我调用getName时,我都必须包括上面描述的检查。

我们来谈谈getName如果我们在该函数中允许NULL学生,我们该怎么办?由于我们无法得到我们没有的学生的名字......这很难说。我们输出错误了吗?提出异常?杀了这个程序?

在成员函数中,期望您正在操作的对象存在是公平的游戏。如果没有,那么调用者在NULL指针上执行->getName()时会出现错误。同样,在正常功能中,我们可以参考:

void do_something_with_a_student(Student &s)

引用不应该为NULL,因此允许此函数假定它始终具有有效学生。你唯一需要担心的地方是“我有一个有效的学生吗?”是逻辑上出现这种关注的地方,例如刚刚搜索过学生。如果我在班级名单中搜索“比利”,我可能会或可能不会有这样的学生:在这里,我们需要“不存在”的价值; NULL对此很有用。

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

答案 1 :(得分:0)

指针不是对象。它是一个对象的地址。就像一个街道地址。

当你调用new MyObject()时,会发生两件事:创建一个对象(某处),然后返回该对象的地址。

值为null的指针是一个读取“无此地址”的地址,或者是一个空白地址。当你使用这个空指针时,你会告诉语言“我保证这个地址是好的,现在使用它”。由于您的保证是谎言,因此会导致未定义的行为(通常是崩溃,但无法保证)。

空白地址没有成员函数或数据来获取值。你所做的相当于告诉盲人飞行员将他们的坐标设置在太平洋中部,将他们的飞机降落在那里,然后下车,走进30英尺外的房子,然后拿起柜台上的香蕉(下一个)到烤箱)。

没有香蕉,没有房子,也没有跑道告诉飞行员。所以情况很糟糕。

答案 2 :(得分:0)

设置指向null的指针是一种重置&#34;重置&#34;一个指针,指向什么都没有。当您使用newStudent-&gt; getName()时,您正在访问newStudent类并在其中调用函数。如果newStudent指向什么都没有,那么它内部没有函数可以调用,因为它甚至没有指向任何东西。一旦给它指向一个类对象,就可以在其中调用getName()函数。您可以将NULL(nullptr)视为与#34相同;我指向NULL,因此您不能也不应该将我用于任何事情,因为此处没有任何内容可以以&#34;开头。您可以通过执行&#34; pointer == nullptr&#34;来测试它。记住,你不能假设任何关于NULL的东西,它不是零,它不是任何东西,因为它是未定义的,除非它是NULL。