本段镜像:C++ 声明,定义与初始化 - JJ_S - 博客园 —— 排版和补充
声明、定义和初始化是什么?
定义:用于为变量分配存储空间,用于存放对应类型的数据,变量名就是对相应的内存单元的命名,还可为变量指定初始值。
- 定义也是声明;
- 程序中,变量有且仅有一个定义。
- 声明:
- 同一个变量可以声明多次;
- 初始化:
- 补充:在C++11之后,为了形式上区分声明和初始化,
声明、定义和初始化的表示法
声明形如:变量的extern声明,函数声明,类和模板类的声明,只是进行了声明但未进行定义;
extern int x; // 对象object声明(extern声明)std::size_t numDigits(int number); // 函数声明class widget; // class声明template<typename T> class GraphNode; // 模板template声明
定义
- 定义也是声明,extern声明不是定义,即不分配存储空间。extern告诉编译器变量在其他地方定义了。
- 如果声明有初始化式,就被当作定义,即使前面加了extern。只有当extern声明位于函数外部时,才可以被初始化。
- 函数的声明和定义区别比较简单,带有{ }的就是定义,否则就是声明。
int i; // 既是声明,也是定义extern double pi=3.1416; // 定义int func(int a, int b) { return a + b; } // 函数定义
变量设计指导
- 公共的变量声明可以放到头文件,在使用的地方包含这个头文件就声明了这个变量。
- 解释:这实际上像接口一样向外部提供了变量/常量;使用第三方代码,就可以包含其头文件;
- 变量的定义通常不要放到头文件,会导致重复定义错误;
- 例外:值在编译时就已知的const变量的定义可以放到头文件中;
- 函数的定义通常不要放到头文件中,应当在头文件中声明,在源文件中定义;
- 例外:模板函数相反,模板函数的定义应当放到头文件中,而不是和普通函数一样在头文件中声明,在源文件中定义;
- 例外:内联函数的定义应当放到头文件中,而不是和普通函数一样在头文件中声明,在源文件中定义;
- 解释:对于模板函数,模板函数的实现写在头文件里,才能保证编译器在编译每个包含该头文件的目标时都能依据模板实现来生成具体类型的函数实现;(当然,也可以通过在源文件中添加形如template char func(char,char);的声明来显式指定生成哪些类型的函数实现;但是这样一点也不实用)
- ref:如果非要在头文件中定义模板函数 C++ 在.h文件中声明,在.cpp文件中定义 模板函数和模板类_Expressing Youself Using Code-CSDN博客
- 解释:对于内联函数,如果一个内联函数会在多个源文件中被用到,那么必须把它定义在头文件中;这是由于如果内联函数定义在头文件中,所有包含该头文件的编译单元都可以正确找到函数定义。如果内联函数fun()定义在某个编译单元A中,那么其他编译单元中调用fun()的地方将无法解析该符号,因为在编译单元A生成目标文件A.obj后,内联函数fun()已经被替换掉,A.obj中不再有fun这个符号(考证:内联函数不会出现在符号表中),链接器自然无法解析。
- 类型的定义应当放在头文件中,除非是故意为之:结构的定义/联合的定义/类的定义/模板类的定义/using和typedef的定义类型;
深入理解初始化
C++11使用{}大括号初始化 - adfas - 博客园 许多语法概念都是有编译器背景的;
大括号之谜:C++的列表初始化语法解析 - 知乎
- 到了C++11,到底应该用什么初始化?等号、圆括号还是大括号?C++创建对象时区分圆括号( )和大括号{ } - 知乎
C++11主张在表示法上用大括号“统一初始化”
为什么使用大括号?
- 列表初始化本来就要使用大括号,其他各种各样的初始化都使用大括号,终于可以统一表示法;
- 当大括号初始化用于内置类型的变量时,如果我们初始值存在丢失信息的风险,则编译器将报错;(因为使用大括号,编译器得以区分,这既不是像等号一样故意进行赋值,也不是圆括号一样调用类型转换函数,而是明确的初始化语义)
为什么不使用圆括号?
- 当表达类内成员的就地初始化时,等号和大括号都可以使用,不能使用圆括号,因为会和函数声明有歧义;
- 当开发者想要一个默认构造的对象时,程序会不经意地声明个函数而不是构造对象
为什么不使用等号?
- 希望在形式上与赋值操作区分开;
- 不可拷贝对象(例如,std::atomic)可以用大括号和圆括号初始化,但不能用等号;(考证—这种设计本质上也是由于要实现不可拷贝,删除了拷贝运算符)
initial_list:待补充
