值为NULL的对象指针
相信大家对NULL不会很陌生,NULL 是一个标准规定的宏定义,用来表示空指针常量,当一个指针变量被赋值为NULL时,表示它不再指向任何有效地址,无法在访问任何数据。在VS2012库文件stdio.h中有如下定义:
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
NULL经常被用作指针初始化行为,以及free/delete某一指针后对其的赋值操作,这都是我们所鼓励的一些好的习惯操作。在对NULL指针进行解引用或者指向操作,会出现coredump或者其他undefined行为,而如果delete NULL指针的操作则是合法的。现在来看文中提到的值为NULL的对象指针,如下面的例子:
class A
{
public:
void foo()
{
cout<<"call foo"<<endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A *p = NULL;
p->foo(); return ;
}
这里使用NULL指针p,调用成员函数foo(),运行时程序输出了“call foo",而没有coredump或者出现别的undefined行为,这是怎么回事呢?我们知道,对于类中的成员函数,隶属于所有类的对象,程序编译后,成员函数的地址已经确定,而成员函数访问类中的成员变量时,需要借助this指针对各个对象进行区分。看到类A的成员函数foo原型为void A::foo();,编译器会在发生函数在调用时,将其解释为void A::foo(A *const this);。注意这里的参数,this指针表示调用此函数的类A的对象地址。对于调用端,编译器将进行如下解释:
A a;
a.foo(); //->foo(&a);
A *p = NULL;
p->foo(); //->foo(p);
例子中传入的p为NULL,而在成员函数foo中,并未有用到通过this指针访问成员变量的语句,所以通过NULL指针调用foo时,得到了正确的结果。如果我们改一下类A的定义,添加成员变量,并且在foo中进行访问,如下:
class A
{
public:
void foo()
{
cout<<"call foo"<<endl;
cout<<"value = "<<value<<endl;
}
private:
int value;
};
这是仍然用p(NULL)指针来调用成员函数foo,则会使程序崩溃,因为编译器会将foo解释为:
void A::foo(A *const this)
{
cout<<"call foo"<<endl;
cout<<"value = "<<this->value<<endl;
}
这里传入的this为NULL,函数对NULL指针进行了指向操作,产生了为undefined行为。
this指针使得类的成员函数可以区分开各个对象之间的成员变量,那么如果类的成员变量属于类而不是属于类的对象呢,即static类型的成员对象
相信大家对NULL不会很陌生,NULL 是一个标准规定的宏定义,用来表示空指针常量,当一个指针变量被赋值为NULL时,表示它不再指向任何有效地址,无法在访问任何数据。在VS2012库文件stdio.h中有如下定义:
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
NULL经常被用作指针初始化行为,以及free/delete某一指针后对其的赋值操作,这都是我们所鼓励的一些好的习惯操作。在对NULL指针进行解引用或者指向操作,会出现coredump或者其他undefined行为,而如果delete NULL指针的操作则是合法的。现在来看文中提到的值为NULL的对象指针,如下面的例子:
class A
{
public:
void foo()
{
cout<<"call foo"<<endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A *p = NULL;
p->foo(); return ;
}
这里使用NULL指针p,调用成员函数foo(),运行时程序输出了“call foo",而没有coredump或者出现别的undefined行为,这是怎么回事呢?我们知道,对于类中的成员函数,隶属于所有类的对象,程序编译后,成员函数的地址已经确定,而成员函数访问类中的成员变量时,需要借助this指针对各个对象进行区分。看到类A的成员函数foo原型为void A::foo();,编译器会在发生函数在调用时,将其解释为void A::foo(A *const this);。注意这里的参数,this指针表示调用此函数的类A的对象地址。对于调用端,编译器将进行如下解释:
A a;
a.foo(); //->foo(&a);
A *p = NULL;
p->foo(); //->foo(p);
例子中传入的p为NULL,而在成员函数foo中,并未有用到通过this指针访问成员变量的语句,所以通过NULL指针调用foo时,得到了正确的结果。如果我们改一下类A的定义,添加成员变量,并且在foo中进行访问,如下:
class A
{
public:
void foo()
{
cout<<"call foo"<<endl;
cout<<"value = "<<value<<endl;
}
private:
int value;
};
这是仍然用p(NULL)指针来调用成员函数foo,则会使程序崩溃,因为编译器会将foo解释为:
void A::foo(A *const this)
{
cout<<"call foo"<<endl;
cout<<"value = "<<this->value<<endl;
}
这里传入的this为NULL,函数对NULL指针进行了指向操作,产生了为undefined行为。
对于C++中类的静态函数,它只能访问类的静态成员变量,因为在编译器对静态函数的解释不会包含this指针,根据这种特性,我们知道NULL指针同样可以调用类中的静态成员函数,以此访问静态成员变量:
class A
{
public:
static void foo()
{
cout<<"call foo"<<endl;
cout<<"value = "<<value<<endl;
}
private:
static int value;
};
int A::value = ; int _tmain(int argc, _TCHAR* argv[])
{
A *p = NULL;
p->foo();
return ;
}
我们知道类的成员函数中可以定义普通函数、静态函数、还可以定义虚函数,已实现多态的效果,那么对于类中的虚函数,用NULL指针调用时会有怎样的后果呢,将上面的代码简单修改下:
class A
{
public:
virtual void foo()
{
cout<<"call foo"<<endl;
}
private:
int value;
};
int _tmain(int argc, _TCHAR* argv[])
{
A *p = NULL;
p->foo(); return ;
}
运行的结果竟然崩溃,foo函数和之前一样,没有访问成员变量value,不会出现NULL指针指向的问题,但程序为何崩溃了呢?原因在于foo函数被定义为了虚函数,这些虚函数中的地址(函数指针)保存在虚函数表中,可以将它看做一个函数指针数组(列表),含有虚函数的类拥有一张虚函数表,而此类的每一个对象在构造时,由编译器将一个指向虚函数表的指针安插进来,成为虚表指针,而它的作用是在我们调用虚函数时,能更准确的访问到虚函数表中此函数的指针,从而进行调用。回过头来看看之前的例子,我们根据类A的NULL指针p调用它的虚函数,而此时由于没有进行初始化,所以虚指针没有安插进去,所以程序崩溃。
值为NULL的对象指针的更多相关文章
- @JsonInclude注解,RestTemplate传输值为null的属性,利用FastJson将属性中有空值null的对象转化成Json字符串
一个pojo类: import lombok.Data; @Data public class Friend { private String name; private int age; priva ...
- spring mvc 处理pojo传递对象时该对象继承父类的属性在网络接收端接收该属性值总是null,why?
//=========================== 情形一: ===============================//在网络上传递User1类对象时info属性值在网络的另一端能够接 ...
- java对象json序列化时忽略值为null的属性
环境: jdk: openjdk11 操作系统: windows 10教育版1903 目的: 如题,当一个对象里有些属性值为null 的不想参与json序列化时,可以添加如下注解 import com ...
- mybatis查询返回的对象不为null,但是属性值为null
返回的对象不为null,但是属性值为null 代码如下: <resultMap id="BaseResultMap" type="com.trhui.ebook.d ...
- js对象转换为json格式时,js对象属性中有值为null和undefined注意事项
当属性值为null时: 当属性值为undefined时: 只有当属性值为未定义时, js对象转换成json格式时会忽略该属性.
- 黑马程序员-nil Nil NULL NSNull 野指针和空指针
空指针1.空指针指不含有任何内存地址的指针.在没有具体初始化之前,其被符值为0Dog * dog = nil;Dog * dog = NULL;都为空指针2.野指针指指向的内存为垃圾内存,导致其值不确 ...
- c++中的对象引用(object reference)与对象指针的区别
★ 相同点: 1. 都是地址的概念: 指针指向一块内存,它的内容是所指内存的地址:引用是某块内存的别名. ★ 区别: 1. 指针是一个实体,而引用仅是个别名: 2. 引用使用时无需解引用(*),指针需 ...
- delete指针以后应赋值为NULL——QT deletelater指针以后也同样要马上赋值为NULL
delete p后,只是释放了指针指向的内存空间.p并不会自动被置为NULL,而且指针还在,同时还指向了之前的地址 delete NULL编译器不会报错(因为delete空指针是合法的) 例: 对一个 ...
- c++值传递和引用及指针传递区别
以下程序各有何问题? ***************************************************************************************** ...
随机推荐
- java 正则表达式提取html纯文本
本文来自我的个人博客: java 正则表达式提取html纯文本 做内容的大家都知道,从html中直接提取纯文本是一个非常大的问题.现将我做的正则匹配贴上: import java.util.regex ...
- InstallShield安装包中集成第三方安装包的方案选择
原文:InstallShield安装包中集成第三方安装包的方案选择[转] 我们在制作安装包时,有些情况下会涉及第三方安装的集成,这里将讨论如何调用安装第三方包,以及需要注意的事项. 第三方安装包的 ...
- openWRT自学---自己编译的第一个 backfire10.03 版本的过程记录(转)
基于 backfire10.03(从http://downloads.openwrt.org/backfire/10.03/ 中下砸的源码包backfire_10.03_source.tar.bz2: ...
- 一个Bug的反思
对输入数据判重,使用Map,将对象作为Key,使用map的containsKey方法来着重是否是重复记录.正常的处理流程:(1)将输入的数据封装成对象(2)判重,将没有重复的数据存储到map(3)对没 ...
- 编程算法 - 数丑陋 代码(C)
数丑陋 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 我们把仅仅包括因子2, 3 和 5的数称作丑数. 求按从小到大的顺序的第5个丑数. 能够 ...
- 加州理工大学公开课:机器学习与数据挖掘_线性模型 II(第IX类)
课程简要: 主要内容包括线性分类和回归分析简单的回忆.除了Logistic回归分析,具体解说误差测量和算法三方面,同时归纳法的非线性变换的分析. 课程大纲: 1.Review 2.Nonlinear ...
- 一个由proguard与fastJson引起的血案(转)
更新微信sdk导致ComposeData中的内部类ComposeDataSender方法被混淆 根本原因,fastjson使用姿势不对. 问题描述: 一个发件人列表里,应当呈现的数据(这里命名为Com ...
- sk_buff整理笔记(两、操作函数)
承接上一:sk_buff 整理笔记(一.数据结构)这一篇要讲的是内核为sk_buff结构提供的一些操作函数. 第一.首先要讲的是sk_buff中的四大指针: 四大指针各自是:head.data.tai ...
- asp.net学习之GridView事件、GridViewRow对象
原文:asp.net学习之GridView事件.GridViewRow对象 1. GridView控件的事件 GridView有很多事件,事件可以定制控件的外观或者行为.事件分为三类 1.1 ...
- ASP.NET MVC上传文件----uploadify的使用
课程设计需要实现上传文件模块,本来ASP.NET是有内置的控件,但是ASP.NET MVC没有,所以就有两种方法:自定义和采用第三方插件.由于时间的关系,故采用第三方插件:uploadify. upl ...