对罗嗦的标题感到抱歉。
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;
}
答案 0 :(得分:3)
不要将指针上的name
和grade
视为设置为某事的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。 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::list
和std::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。