67.变量

1.变量定义

术语:何为对象?

   C++程序员们在很多场合都会使用对象(object)这个名词。通常情况下,对象是指一块能存储数据并具有某种类型的内存空间。一些人仅在与类有关的场景下才使用“对象”这个词。另一些人则已把命名的对象和未命名的对象区分开来,他们把命名了的对象叫做变量。还有一些人把对象和值区分开来,其中对象指能被程序修改的数据,而值(value)指只读的数据。本书遵循大多数人的习惯用法,即认为对象是具有某种数据类型的内存空间。我们在使用对象这个词时,并不严格区分是类还是内置类型,也不区分是否命名或是否只读。

1.1初始值

  初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代。

1.2列表初始化

  C++语言定义了初始化的好几种不同形式,这也是初始化问题复杂性的一个体现。例如,想要定义一个名为units_sold的int变量并初始化为0,以下的4条语句都可以做到这点:

int units_sold = O; 
int units_sold = {0};
int units_sold{0};
int units_sold(0);

 当用于内置类型的变量时,这种初始化形式有一个重要特点:如果我们使用列表初始化且初始值存在丢失信息的风险,则编译器将报错:

long double ld = 3.1415926536; 
int a{ld}, b = {ld};//错误:转换未执行,因为存在丢失信息的危险
int c(ld), d = ld;//正确:转换执行,且确实丢失了部分值

1.3默认初始化

  如果定义变量时没有指定初值,则变量被默认初始化(default initialized),此时变量被赋予了 “默认值"。默认值到底是什么由变量类型决定,同时定义变量的位置也会对此有影响。

 如果是内置类型的变量未被显式初始化, 它的值由定义的位置决定。 定义于任何函数体之外的变量被初始化为0。 然而如6.1.1节(C++ Primer第185页)所示, 一种例外情况是, 定义在函数体内部的内置类型变量将不被初始化(uninitialized)。 一个未被初始化的内置类型变量的值是未定义的(参见C++ Primer 2.1.2节, 第33页), 如果试图拷贝或以其他形式访问此类值将引发错误。
  每个类各自决定其初始化对象的方式。 而且, 是否允许不经初始化就定义对象也由类自己决定。 如果类允许这种行为, 它将决定对象的初始值到底是什么。

  绝大多数类都支持无须显式初始化而定义对象, 这样的类提供了一个合适的默认值。 例如,以刚刚所见为例,string类规定如果没有指定初值则生成一个空串:

std::string empty; //empty非显式地初始化为一个空串
Sales_item item;//被默认初始化的Sales_item对象

  一些类要求每个对象都显式初始化,此时如果创建了一个该类的对象而未对其做明确的初始化操作,将引发错误。

注意:

定义于函数体内的内置类型的对象如果没有初始化,则其值未定义。类的对象如果没有显式地初始化,则其值由类确定。

2.变量声明和定义的关系

  变量声明规定了变量的类型和名字, 在这一点上定义与之相同。 但是除此之外,定义还申请存储空间,也可能会为变量赋一个初始值。如果想声明一个变量而非定义它, 就在变量名前添加关键字extern,而且不要显式地初始化变量:

extern int i;//声明 l. 而非定义i
int j;//声明并定义j

  任何包含了显式初始化的声明即成为定义。我们能给由extern关键字标记的变量赋一个初始值,但是这么做也就抵消了extern的作用。extern语句如果包含初始值就不再是声明, 而变成定义了:

在函数体内部, 如果试图初始化一个由extern关键字标记的变量,将引发错误。

extern double pi = 3.1416;//定义

在函数体内部, 如果试图初始化一个由extern关键字标记的变量,将引发错误。

注意:

变量能且只能被定义一次,但是可以被多次声明。

  声明和定义的区别看起来也许微不足道,但实际上却非常重要。如果要在多个文件中使用同一个变量,就必须将声明和定义分离。此时,变量的定义必须出现在且只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义。

关键概念:静态类型

   C++是一种静态类型(statically typed)语言,其含义是在编译阶段检查类型。其中,检查类型的过程称为类型检查(type checking)。
   我们已经知道,对象的类型决定了对象所能参与的运算。在C++语言中,编译器负责检查数据类型是否支持要执行的运算,如果试图执行类型不支持的运算,编译器将报错并且不会生成可执行文件。
   程序越复杂,静态类型检查越有助于发现问题。然而,前提是编译器必须知道每一个实体对象的类型,这就要求我们在使用某个变量之前必须声明其类型。

3.标识符

  C++的标识符(identifier)由字母、数字和下画线组成,其中必须以字母或下画线开头。标识符的长度没有限制,但是对大小写字母敏感:

//定义4个不同的int变量
int somename, someName, SomeName, SOMENAME; 

  C++语言保留一了些名字供语言本身使用,这些名字不能被用作标识符。

  同时,C++也为标准库保留了一些名字。用户自定义的标识符中不能连续出现两个下画线,也不能以下画线紧连大写字母开头。此外,定义在函数体外的标识符不能以下画线开头。

变量命名规范

  变量命名有许多约定俗成的规范,下面的这些规范能有效提高程序的可读性:

●标识符要能体现实际含义。

●变量名一般用小写字母,如index,不要使用Index或INDEX。

●用户自定义的类名一般以大写字母开头,如Sales_item。

●如果标识符由多个单词组成,则单词间应有明显区分,如student_loan或 studentLoan,不要使用studentloan。

对于命名规范未说,若能坚持,必将有效。

4.名字作用域

  不论是在程序的什么位置,使用到的每个名字都会指向一个特定的实体:变量、函数、类型等。 然而,同一个名字如果出现在程序的不同位置,也可能指向的是不同实体。

  作用域(scope)是程序的一部分,在其中名字有其特定的含义。C++语言中入多数作用域都以花括号分隔。

  同一个名字在不同的作用域中可能指向不同的实体。名字的有效区域始于名字的声明语句,以声明语句所在的作用域末端为结束。

建议: 当你第一次使用变量时再定义它

    一般来说,在对象第一次被使用的地方附近定义它是一种好的选择,因为这样做有 助于更容易地找到变量的定义。更重要的是,当变量的定义与它第一次被使用的地方很近时,我们也会赋给它一个比较合理的初始值。

嵌套的作用域

  作用域能彼此包含,被包含(或者说被嵌套)的作用域称为内层作用域(inner scope),包含着别的作用域的作用域称为外层作用域(outer scope)。作用域中一旦声明了某个名字,它所嵌套着的所有作用域中都能访问该名字。同时,允许在内层作用域中重新定义外层作用域已有的名字:

posted @ 2023-03-16 16:17  CodeMagicianT  阅读(62)  评论(0编辑  收藏  举报