《C++回顾笔记》声明、定义与初始化
声明(Declaration)
所谓声明,是告诉编译器某个东西的名称和类型,但略去某些细节。在程序中,允许重复声明多次。下面的都是声明式:
1 |
|
- 任何包含了显式初始化的声明即成为定义,可以给由
extern
关键字标记的变量赋一个初始值,但是这么做也就抵消了extern的作用,也就不再是声明,而变成定义了:
1
extern int x = 0; /// 定义了一个变量,其类型为int
- 此外,只有当
extern
声明位于函数外部时,才能执行赋值操作。
定义(Definition)
  定义的任务是,为编译器提供声明所遗漏的细节。对于对象而言,定义的任务是为此对象分配内存;对函数或者模板函数而言,定义提供了函数的实现;而对类或者模板类来说,定义列出其成员变量和成员函数。在程序中,只允许定义一次。下面的都是定义式:
1 |
|
大多数情况下,定义也完成了声明的工作,不需要再额外声明。但以下两种情况,定义只是定义:
- 在类定义之外,定义并初始化一个静态数据成员
- 在类外定义非内联成员函数
声明和定义的关系
  为了允许把程序拆分成多个逻辑部分来编写,C++语言支持分离式编译(separate compilation)
机制,该机制允许将程序分割为若干个文件,每个文件可被独立编译。为了支持分离式编译,C++语言将声明和定义区分开来。声明告诉程序一个名字,一个文件如果想使用别处定义的变量则必须包含对那个名字的声明,而定义负责创建与名字关联的实体。
- 程序使用到的每个名字都会指向一个特定的实体:
变量
、函数
和类型
等- 变量声明规定了变量的类型和名字,在这一点上定义与之相同。但是除此之外,定义还申请存储空间,也可能会为变量赋一个初始值。
  声明和定义的区别看起来也许微不足道,但实际上却非常重要。如果要在多个文件中使用同一个变量,就必须将声明和定义分离。此时,变量的定义必须出现在且只能出现在一个文件中
,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义。
初始化(Initialization)
  初始化是指给予已定义的对象初始值
的过程。如果定义变量时候没有初始化,则变量将会被默认初始化(隐式初始化,由编译器按照一定的规则自动完成)
。一般来说,默认初始化的规则如下:
- 栈中的变量
(函数内定义的变量)
和堆中的变量(动态内存)
会有不确定的值 - 全局变量和静态变量
(包括局部静态变量)
会被初始化为该变量的类型中0
这个概念对应的值C++11: If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an object with automatic or dynamic storage duration has indeterminate value. Note: Objects with static or thread storage duration are zero-initialized.
  之所以对不同类型的变量,默认初始化的行为会不一致,是因为编译器存储它们的内存空间不同。关于编译器存储变量的内存空间分配,可以参阅进程的内存空间分配。
  值得注意的是,如果变量的类型是自定义的类
,那么该变量在被默认初始化的时候,将会调用其默认构造函数。倘若该类找不到默认构造函数,或者没有带缺省参数的构造函数,编译器将会报错。
对于自定义的类来说,其没有初始化的成员变量的值是否为一个不确定的值,取决于其所在对象的存储方式
(对象也没被初始化)
:
- 如果对象为全局变量或者静态变量(存于BSS段),那么其成员变量的值将会被初始化为0
- 如果对象为动态对象或者局部变量(存于堆或者栈上),那么其成员变量的值将是一个不确定的值
1 |
|
  根据默认初始化的规则,不难得到上述事例程序的输出为:
1 |
|
赋值与初始化
  在C++中,可以使用=
来完成初始化,因此这很容易让人认为初始化是赋值的一种。然而,事实上,在C++语言中,赋值和初始化是两个完全不同的操作,只是它们两者的区别在很多情况下可以忽略不计。需要强调的是,初始化不是赋值,初始化的含义是创建变量的时候赋予其一个初始值,而赋值的含义则是把变量的当前值擦除,并使用一个新值来替代。