C++中的空指针

C++中的空指针,c++,pointers,C++,Pointers,我有几个关于指针的问题。第一: ObjectType *p; p->writeSomething(); 指针还没有初始化时,为什么可以调用对象上的方法?如果我运行该代码,我将从控制台窗口中的WriteMething获得输出。第二: ObjectType *p; if(p==NULL) cout<<"Null pointer";//This is printed out p = new ObjectType; delete p; if(p==NULL) cout&

我有几个关于指针的问题。第一:

 ObjectType *p; 
 p->writeSomething();
指针还没有初始化时,为什么可以调用对象上的方法?如果我运行该代码,我将从控制台窗口中的WriteMething获得输出。第二:

ObjectType *p;
if(p==NULL) 
cout<<"Null pointer";//This is printed out
p = new ObjectType;
delete p;
if(p==NULL)
   cout<<"Null pointer";
else
   cout<<"Pointer is not null";//This is printed out
为什么第二个if语句中的指针不为null?如何检查指针是否未指向任何内存地址?我还想知道,当程序执行完毕时,是否有任何方法可以检查一些内存是否没有被释放。例如,如果您忘记在代码中编写一条delete语句。

第一条代码是未定义的行为,任何事情都可能发生,甚至看起来是正常的。这可能是因为调用是静态解析的,并且您没有访问该类的任何成员

对于第二个代码段,delete没有将指针设置为NULL,它只是释放内存。指针现在悬空,因为它指向的是您不再拥有的内存。

第一个代码是未定义的行为,任何事情都可能发生,即使看起来正常。这可能是因为调用是静态解析的,并且您没有访问该类的任何成员

delete p;
对于第二个代码段,delete没有将指针设置为NULL,它只是释放内存。指针现在悬空,因为它指向您不再拥有的内存

delete p;
释放内存,但不会更改存储在p中的地址值

<>标准C++中没有检测指针引用无效内存的方法。您有责任不取消引用无效指针

第一个例子是未定义的行为。未定义行为的一个可能结果是程序按照您的预期方式工作。同样,您有责任不使用未定义的行为编写程序

在您的代码中,WriteMething可能是一个非虚拟成员函数,它不会反引用这个函数,这就是为什么它在编译器上对您起作用的原因。如果您试图引用某些成员数据字段,很可能会遇到运行时错误

释放内存,但不会更改存储在p中的地址值

<>标准C++中没有检测指针引用无效内存的方法。您有责任不取消引用无效指针

第一个例子是未定义的行为。未定义行为的一个可能结果是程序按照您的预期方式工作。同样,您有责任不使用未定义的行为编写程序

在您的代码中,WriteMething可能是一个非虚拟成员函数,它不会反引用这个函数,这就是为什么它在编译器上对您起作用的原因。如果您试图引用某些成员数据字段,很可能会遇到运行时错误。

delete将调用ObjectType的析构函数,然后取消分配内存,但它不会显式使指针为空

作为一个好的编程实践,您必须这样做

delete将调用ObjectType的析构函数,然后取消分配内存,但它不会显式地使指针为NULL


作为一个好的编程实践,您必须这样做

您的代码当然会表现出未定义的行为,但下面是一个示例,说明了为什么即使没有对象也可以调用成员函数:如果成员函数不引用类的任何成员对象,那么它将永远不需要访问内存中未初始化的任何部分。这意味着,您的成员函数本质上是静态的

如您所知,成员函数可以被视为具有隐式实例对象引用参数的普通自由函数。例如,一个定义如下的简单类Foo

struct Foo
{
    void bar() { std::cout << "Hello\n"; }
};
可以作为一个单独的自由函数来实现:

void __Foo_bar(Foo * this)
{
    std::cout << "Hello\n";
}
现在当你说Foo*p;p->bar;,这相当于一个自由函数调用;。您最终会向函数传递一个无效指针,但由于函数从未使用该指针,因此不会造成任何伤害


另一方面,如果类有一个成员对象,如int Foo::n;,如果成员函数试图访问它,那么您的实现将尝试访问此->n,这很可能会立即导致问题,因为您实际上是在取消引用无效指针。

您的代码当然会表现出未定义的行为,但下面是一个例子,说明了为什么即使没有对象,也可以调用成员函数:如果成员函数不引用类的任何成员对象,那么它将永远不需要访问内存中未初始化的任何部分。这意味着,您的成员函数本质上是静态的

如您所知,成员函数可以被视为具有隐式实例对象引用参数的普通自由函数。例如,一个定义如下的简单类Foo< /p> 可以作为一个单独的自由函数来实现:

void __Foo_bar(Foo * this)
{
    std::cout << "Hello\n";
}
现在当你说Foo*p;p->bar;,这相当于一个自由函数调用;。您最终会向函数传递一个无效指针,但由于函数从未使用该指针,因此不会造成任何伤害


另一方面,如果类有一个成员对象,如int Foo::n;,如果成员函数试图访问它,您的实现将尝试访问此->n,这很可能会导致立即出现问题,因为您实际上正在取消对无效指针的引用。

WriteMething是否可能是静态方法?这些是不使用常规哑指针的一些好理由。有很多智能指针都没有这些恼人的怪癖。@hristoilev I不一定是静态方法。如果它不能访问任何成员,那么它有很好的工作机会。即使它确实访问了成员,它有时仍然可以工作。这是未定义的行为,任何事情都有可能发生。@PaulManta,所以如果我理解正确,如果ObjectType之前的类继承中没有虚拟方法,那么WriteMething通常在编译时解析,p只是作为this参数传递的?然后,如果WriteMething不访问任何非静态成员变量,那么它很可能会在==NULL的情况下工作。@hristoilev如果您调用的不是虚拟方法,则它总是在编译时解析的。是的,指针将以此方式传递,在某些情况下,它可能会工作。但是不要指望它。WriteMething有没有可能是一个静态方法?这些都是不使用常规的、愚蠢的指针的好理由。有很多智能指针都没有这些恼人的怪癖。@hristoilev I不一定是静态方法。如果它不能访问任何成员,那么它有很好的工作机会。即使它确实访问了成员,它有时仍然可以工作。这是未定义的行为,任何事情都有可能发生。@PaulManta,所以如果我理解正确,如果ObjectType之前的类继承中没有虚拟方法,那么WriteMething通常在编译时解析,p只是作为this参数传递的?然后,如果WriteMething不访问任何非静态成员变量,那么它很可能会在==NULL的情况下工作。@hristoilev如果您调用的不是虚拟方法,则它总是在编译时解析的。是的,指针将以此方式传递,在某些情况下,它可能会工作。但不要指望它。@user1163392取决于它。有时,您不必这样做,但大多数情况下,如果必须使用指针,可以将它们设置为NULL。但是尽可能使用智能指针。@DavidSchwartz如果代码的某部分删除了另一部分正在使用的内存,那么这是设计中的一个缺陷。这与将指针设置为null无关。@PaulManta:因为您可以通过另一个指针轻松访问它,所以这并不重要。由于这将使错误的删除以静默方式成功,因此它隐藏的错误与它暴露的错误一样多。@PaulManta:如果代码后面有不应该存在的删除,将指针设置为NULL将使删除在某些时候以静默方式成功。它甚至可能鼓励你把它放在那里,以防泄漏。这是无害的,对吧?如果对象已经被删除,指针是空的,对吗?然后,在失败的情况下,在其他地方删除对象,当然不能将该指针设置为NULL,代码将严重失败。有时,您不必这样做,但大多数情况下,如果必须使用指针,可以将它们设置为NULL。但是尽可能使用智能指针。@DavidSchwartz如果代码的某部分删除了另一部分正在使用的内存,那么这是设计中的一个缺陷。这与将指针设置为null无关。@PaulManta:因为您可以通过另一个指针轻松访问它,所以这并不重要。由于这将使错误的删除以静默方式成功,因此它隐藏的错误与它暴露的错误一样多。@PaulManta:如果代码后面有不应该存在的删除,将指针设置为NULL将使删除在某些时候以静默方式成功。它甚至可能鼓励你把它放在那里,以防泄漏。这是无害的,对吧?如果对象已经被删除,指针是空的,对吗?然后,在失败的情况下,对象被删除到其他地方,当然不能将这个指针设置为NULL,代码将非常失败