C++primer学习笔记
第一部分 c++基础
第二章 变量和基本类型
2.1基本内置类型
类型 | 含义 | 最小尺寸 |
---|---|---|
bool | 布尔类型 | 8bits |
char | 字符 | 8bits |
wchar_t | 宽字符 | 16bits |
char16_t | Unicode字符 | 16bits |
char32_t | Unicode字符 | 32bits |
short | 短整型 | 16bits |
int | 整型 | 16bits (在32位机器中是32bits) |
long | 长整型 | 32bits |
long long | 长整型 | 64bits (是在C++11中新定义的) |
如何选择类型
1.当知晓数值不能为负数时,选用无符号类型
2.使用int执行整数运算,表示范围超过int时,选择long long类型
3.在算数表达式中不要使用char或者bool
4.浮点数选用double
类型转换
1.当赋给无符号类型一个超过它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数
2.当赋给带符号类型一个超过它表示范围的值时,结果可能是未定义的,此时程序可能会崩溃或继续工作,也可能会产生垃圾数据
3.如果表达式既有无符号类型,也有有符号类型,带符号类型取值为负出现异常,因为带符号数会自动转换为无符号数
unsigned char c = -1; //假设char占8比特,c的值为255,-1%256=255,注意,不同编译器结果不同,这里取正余数
signed char c2 = 236; //c2的值是未定义的
字面值常量
一个形如42的值称作字面值常量,一望而知,每个字面值常量对应一种数据类型
2.2 变量
变量定义和初始化
1.变量定义的基本类型:随后紧跟一个或者多个变量名组成的列表
2.在C++中初始化和赋值是两个不同的操作,初始化的含义是创建变量时赋予一个初始值,赋值是把当前值擦除,使用一个新值来代替
3.列表初始化**(C++11)**,使用花括号来初始化变量称为列表初始化,但是有一个重要特点,如果使用列表初始化存在丢失信息的风险,编译器将报错:
long double ld = 3.14
int a{ld},b={ld} //报错,因为存在丢失信息的风险
4.默认初始化,内置类型的变量未被显式初始化,它的值由定义的位置决定,定义域任何函数体之外被初始化为0,在函数体内部,则不被初始化,其值是未定义的。
变量申明和变量的定义
C++将变量的申明和定义分开,定义负责创建与名字关联的实体,申明使得名字为程序所知,如果申明一个变量而不是定义,使用extern关键字,而且不要显示初始化变量,如果给extern关键字标记的变量赋值,那么extern的作用就被抵消了。如果在多个文件中使用中使用同一个变量,就必须将申明和定义区分开
- 名字的作用域(namescope
- 第一次使用变量时再定义它。
- 嵌套的作用域
- 同时存在全局和局部变量时,已定义局部变量的作用域中可用
::reused
显式访问全局变量reused。 - 但是用到全局变量时,尽量不适用重名的局部变量。
- 同时存在全局和局部变量时,已定义局部变量的作用域中可用
变量命名规范
- 需体现实际意义
- 变量名用小写字母
- 自定义类名用大写字母开头:Sales_item
- 标识符由多个单词组成,中间须有明确区分:student_loan或studentLoan,不要用studentloan。
2.3 复合类型
申明语句更通用的描述,一条申明语句由一个基本数据类型和紧随其后的一个申明列表组成
引用
1.引用必须初始化并且指向一个对象,但引用本身不是一个对象,没有实际地址
2.引用初始化后不能解绑,并且只是将一个对象绑定,而不是拷贝
3.引用类型必须和绑定对象严格一致,但是有两种例外情况
- 一种是在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换为引用类型即可
指针
-
指针在定义时没有被初始化,将拥有一个不确定的值
-
引用没有实际地址,不能定义指向引用的指针
-
指针的类型和被指向的对象的类型必须一致
-
得到空指针最好的办法是使用**字面值nullptr****(C++11)**来初始化,尽量避免使用NULL来初始化指针
-
void*指针是一种特殊的指针类型,可以存放任意对象的地址,和正常的指针相比,void*指针的作用:和别的指针作比较、作为函数的输入和输出、给另外一个void*指针赋值,不能直接操作void*指针所指向的对象。
- 建议:初始化所有指针。
2.4 const限定符
-
定义一个不可改变的常量,所以必须const对象必须要初始化且不可改变
-
默认情况下,const对象仅在文件内有效,如果想在对各文件之间共享const对象,必须在变量的定义之前添加extern
const引用
- 可以把引用绑定到const对象上,称为对常量的引用,这样做了之后,便不能通过引用改变引用对象的值
- 不能让一个非常量引用指向常量对象
- 常量引用仅对引用可参与的操作做出了限定,对于引用的对象本身是不是一个常量未作限定。
指针和const
- 指向常量的指针不能用于改变其所指对象的值,要想存放指向常量的地址,只能使用指向常量的指针
const double pi = 3.14
double *ptr = &pi //错误,ptr是一个普通指针
const double *cptr = &pi //正确
- 和常量引用一样,指向常量的指针没有规定所指向的对象必须是一个常量
- 常量指针必须初始化,初始化完成,存放在指针中的地址就不能在改变
//常量指针和指向常量的指针并不相同
int errNumb = 0;
int *const curErr = &errNumb;//这是常量指针
const int *const pip = &errNumb //这是一个指向常量对象的常量指针
顶层const
-
顶层const表示指针本身是一个常量
-
底层const表示指针所指的对象是一个常量
-
更一般的是顶层const可以表示任意的对象是常量,底层const则与指针和引用等符合类型的基本数据结构有关
-
用于申明引用的const都是底层const
-
在执行拷贝操作时,拷入和拷出的对象必须具备相同的底层const资格或者两个对象的数据类型必须能够转换,而顶层const不受影响
constexpr
和常量表达式
- 常量表达式:指值不会改变,且在编译过程中就能得到计算结果的表达式。
C++11
新标准规定,允许将变量声明为constexpr
类型以便由编译器来验证变量的值是否是一个常量的表达式。
2.5 处理类型
类型别名
- 传统别名:使用typedef来定义类型的同义词。
typedef double wages;
- **C++11:**别名声明(alias declaration):
using SI = Sales_item;
// 对于复合类型(指针等)不能代回原式来进行理解
typedef char *pstring; // pstring是char*的别名
const pstring cstr = 0; // 指向char的常量指针
// 如改写为const char *cstr = 0;不正确,为指向const char的指针
// 辅助理解(可代回后加括号)
// const pstring cstr = 0;代回后const (char *) cstr = 0;
// const char *cstr = 0;即为(const char *) cstr = 0;
auto类型说明符
- C++11引入auto类型说明符,auto让编译器通过初始值来推算变量的类型,所以auto定义变量必须初始化
- 在一条语句中如果使用auto申明多个变量,则所有变量的初始基本数据类型必须保持一致
- auto会忽略顶层const,保留底层const
- 如果希望是顶层const需要自己加
const
decltype类型指示符
- **C++11,**从表达式的类型推断出要定义的变量的类型
- decltype:选择并返回操作数的数据类型
decltype(f()) sum = x;
推断sum
的类型是函数f
的返回类型- 不会忽略
顶层const
- 如果对变量加括号,编译器会将其认为是一个表达式,如int i–>(i),则decltype((i))得到结果为int&引用
- 赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型。也就是说,如果 i 是 int,则表达式 i=x 的类型是 int&
- decltype((i)) (注意是双括号)的结果永远是引用,decltype(i) 只有当i本身是一个引用时才是引用
自定义数据结构
C++11规定,可以为数据成员提供一个类内初始值,创建对象时,类内初始值将用于初始化数据成员
编写自己的头文件
- 预处理器(preprocessor):确保头文件多次包含仍能安全工作。
- 当预处理器看到
#include
标记时,会用指定的头文件内容代替#include
- 头文件保护符(header guard):头文件保护符依赖于预处理变量的状态:已定义和未定义。
#indef
已定义时为真#inndef
未定义时为真- 头文件保护符的名称需要唯一,且保持全部大写。养成良好习惯,不论是否该头文件被包含,要加保护符。
- 申明函数原型也放在头文件中,函数实现在源文件中
#ifndef SALES_DATA_H //SALES_DATA_H未定义时为真
#define SALES_DATA_H
strct Sale_data{
...
}
#endif