值为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++值传递和引用及指针传递区别
以下程序各有何问题? ***************************************************************************************** ...
随机推荐
- matlab练习程序(PCA<SVD>)
clear all;close all;clc;img1=imread('Corner.png');img2=imread('Corner1.png');img3=imread('Corner2.pn ...
- 玩转Linux之- CentOS 7.0,启用iptables防火墙
原文 玩转Linux之- CentOS 7.0,启用iptables防火墙 CentOS 7.0默认使用的是firewall作为防火墙,这里改为iptables防火墙. 1.关闭firewall:sy ...
- RSA加密解密(PHP Demo)
$private_key = '-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDpoODVtnSztGyb//p+g/Ob36jb3jzWzS2qovOj ...
- asp.net学习之GridView七种字段
原文:asp.net学习之GridView七种字段 asp.net中GridView绑定到数据源时,可以自动显示数据源的各个字段.只要设定其AutoGenerateColumns为TRUE即可.但这, ...
- requestWindowFeature()应用
我们在开发程序是常常会须要软件全屏显示.自己定义标题(使用button等控件)和其它的需求,今天这一讲就是怎样控制Android应用程序的窗口显示. 首先介绍一个重要方法那就是requestWindo ...
- 于Eclipse在建筑cocos2d-x的Android开发环境(避免cygwin)
于Eclipse在建筑cocos2d-x的Android开发环境(避免cygwin) 在袭击几天学习Android开发之后,我開始转向cocos2d-x的Android开发了.因为自己曾经有了解过co ...
- Ajax.BeginForm的异步提交数据 简介
Html.BeginForm与Ajax.BeginForm都是MVC架构中的表单元素,它们从字面上可以看到区别,即Html.BeginForm是普通的表单提交,而Ajax.BeginForm是支持异步 ...
- android如何在代码中设置margin
1.首先看你要设置组件的父容器是什么. 例如,我是FrameLayout其中包括组件.如果调用bottomLayout这是: FrameLayout.LayoutParams lp = new Fra ...
- java_Timer_schedule jdk自带定时器
定时器经常在项目中用到,定制执行某些操作,比如爬虫就需要定时加载种子等操作,之前一直用spring的定制器近期做项目发现,jdk有很简单的提供 代码如下 1 /* * Copyright (c) 20 ...
- 华为机试 之 joseph环
一:首先科普一下约瑟夫问题的数学方法 (1) 不管是用list实现还是用vector实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比較烦,并且时间复杂度高达O(nm),当n,m很大(比如上百 ...