4.9 值和对象
变量和文字常量都有存储区,并且有相关的类型,区别在于变量是可寻址的,对于每个变量,都有两个值与其相关联:其中一个是数据值,存储在某个内存地址中,也称右值,右值是被读取的值;另一个是地址值,即存储数据值的那块内存地址,也称左值。
对象是类的具体实例或实现。C++中的对象,是用类定义的变量,占用内存单元。
4.9.1 左值和右值
左值(lvalue):指向内存位置的表达式被称为左值(lvalue)表达式。左值可以出现在赋值号的左边或右边。
右值(rvalue):指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。
具体可以理解为,左值更多指的是可以定位,即有地址的值,而右值没有地址。
例如:
在以上代码中,“doc”,“a[0]”,“*(a-8)”这些值,还有函数中的返回值都是左值,因为这些值都是通过具体名字和引用来指定一个对象。“8”,“100”,“25”这些值都是右值,因为没有一个特定的名字引用到这个值。
1. 依据下述规则来判断左值
(1)“通过非函数类型声明的非类型标识符”都是左值。
(2)每种运算符都规定了它的运算结果是否为左值。
2. 常见规则
(1)下列运算符的操作数要求左值:sizeof运算符,取地址运算符“&”,“++”运算符,“--”运算符,赋值“=”运算符的左侧,成员“.”运算符的左侧。
(2)间接运算符*的运算结果是左值;取地址运算符&的运算结果是右值。
(3)下列表达式不能产生左值:数组名、函数、枚举常量、赋值表达式、强制类型转换和函数调用等。
4.9.2 对象的生命周期
静态变量的生命周期是整个程序的生命周期。析构函数析构的是动态申请的内存。而类中的成员变量是在类的对象声明时创建,在对象生存期结束后截止。
C++的new运算和C语言的malloc()函数都是为了配置内存,但前者比之后者的优点是,new不但配置对象所需的内存空间,还会引发构造函数的执行。所谓构造函数,就是对象诞生后第一个执行(并且是自动执行)的函数,它的函数名称必定要与类别名称相同。在后面的章节中会详细介绍。
相对于构造函数,自然就有个析构函数,也就是在对象行将毁灭但未毁灭的前一刻,最后执行(并且是自动执行)的函数,它的函数名称必定要与类别名称相同,再在最前面加一个~符号。
对象的生命周期有以下几种情况:
(1)对于全局对象,程序一开始,其构造式就先被执行;程序即将结束前其析构式被执行。
(2)对于局部对象,当对象诞生时,其构造式被执行;当程序流程将离开该对象的存活范围时,其析构式被执行。
【例4-9】编写程序,在类中定义构造函数和析构函数,创建完对象后再进行销毁。
(1)在Visual Studio 2017中,新建名称为“4-9.cpp”的Project9文件。
(2)在代码编辑区域输入以下代码。
【程序分析】本例中,定义了一个类A,在该类里定义了构造函数和析构函数。在main()函数中创建了两个对象x和y。执行程序时,先执行对象x的构造函数,再执行y的构造函数;接着开始销毁对象,先执行y的析构函数,再执行x的析构函数。所以,对象的销毁顺序是按照弹栈顺序“先进后出”执行。
在Visual Studio 2017中的运行结果如图4-18所示。
图4-18 对象的生命周期
(3)对于静态(static)对象,当对象诞生时其构造式被执行;当程序将结束时其析构式才被执行,但比全局对象的析构式早一步执行。
(4)对于以new方式产生出来的区域对象,当对象诞生时其构造式被执行。析构式则在对象被删除时执行。
C++类里面的变量类型,仅仅是对外部调用的使用和继承时的使用作了规定,关于它们的生命周期,其实和C语言是基本相同的。静态成员变量是有整个程序的生命周期的,而且一个类中的静态成员,无论有多少个对象,使用的都是同一个静态成员。
例如:
实际上
a.x 等价于 b.x