C++ 学习笔记(2)变量和基本类型(复合类型:引用、指针)、const、constexpr、typedef(using)、auto、decltype
参考书籍:《C++ Primer 5th》
2.1 基本内置类型
2.1.1 算术类型
- 类型char和类型signed char并不一样,类型char可能表现形式是带符号或无符号的,具体由编译器决定。所以最好不要直接用char作算术运算。
2.1.2 类型转换
- 当赋给无符号类型一个超过表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。取模和取余的区别
2.1.3 字面值常量
- short 类型没有字面值常量。负数的字面值,负号不在字面值内,只是对字面值取负值而已。
- 字面值 ‘A’ 代表单独的字符,字符串 “A” 包含两个字符:‘A’和’\0’。
2.2 变量
2.2.1 变量定义
- 列表初始化:下面四种方法都可以。同理可用于赋值。
int a = 0;
int a = {0};
int a{0};
int a(0);
- 对于内置类型的变量,使用列表初始化且初始值存在丢失信息风险时,编译器会报错。
long double ld = 3.1415926;
int a{ ld }, b = { ld }; // 编译器报错:“错误 C2397 从“long double”转换到“int”需要收缩转换”
int c(ld), d = ld; // 正确:但确实丢失了精度。
- 定义于任何函数体制外的变量被初始化为0,如全局变量,类成员变量。静态变量除外。
2.2.3 标识符
- C++为标准库保留了一些名字。 用户自定义的标识符中不能连续出现两个下划线,也不能以下划线紧连大写字母开头。定义在函数体外的标识符不能以下划线开头。
2.3 复合类型
2.3.1 引用
- 引用必须初始化:因为无法令引用重新绑定到另一个对象。
- 引用类型的初始值必须为一个对象,常量引用可以绑定字面值。
- 引用不是对象,没有实际地址,所以不能定义指向引用的指针。
2.3.2 指针
-
指针的值(地址)应属于下面4种状态:
- 指向一个对象。
- 指向紧邻对象所占空间的下一个位置。
- 空指针(nullptr或者0)。
- 无效指针。
-
void* 指针:可以存放任意对象的地址。仅仅是存储空间,没办法访问内存空间所存的对象,因为不知道对象是什么类型(void)。
-
指向指针的引用:指针是对象,所以存在对指针的引用
int i = 666;
int *p; // p 是一个 int型指针
int *&r = p; // r 是一个对指针 p 的引用
r = &i; // r 引用了一个指针,因此给 r 赋值 &i 就是令 p 指向 i
*r = 233; // 解引用 r 得到 i,也就是p指向的对象,将 i 的值改为233
2.4 const限定符
- const对象一旦创建后其值就不能改变,所以const对象必须初始化。
- 默认情况下,const对象仅在文件内有效(即使是全局变量)。想要在多个文件共享const对象,必须在变量定义前添加extern关键字。
const int a = 1;
和const int &a = 1;
的区别:这里
2.4.1 const的引用
- 称作对常量的引用。不能修改绑定的对象(对象可以不是常量)。
int a = 233;
const int &b = a; // 正确:可以绑定普通int对象。
const int ci = 1024;
const int &r1 = ci; // 正确:可以绑定常量。
r1 = 18; // 错误:r1是对常量的引用。
int &r2 = ci; // 错误:非常量引用不能指向常量引用。
const int d = 123; // 正确:在编译时,所有用到d变量都会替换为对应值。(变成字面量,而非变量)
const int &d = 123; // 正确:不过还是作为变量,会分配地址。
2.4.2 指针和const
- 普通指针不能指向常量,需要用常量指针。
- 指向常量的指针 和 const指针:(判断方法:从右往左看)
const double pi = 3.1415926;
const double *cptr = π // 指向常量的指针。指针不是常量,指针可以更改,内容不能改。
int a = 0;
int *const b = &a; // const指针。指针是常量,指针不能改,内容可以改。
const double *const pip = π // 指向常量对象的常量指针。指针和内容都不能改。
2.4.3 顶层const 和 底层const
- 顶层const:指针本身是常量。
- 底层const:指针所指对象是常量。
2.4.4 constexpr 和 常量表达式
- 常量表达式:不会改变且编译过程就能得到计算结果的表达式。(如字面量)
- constexpr变量:告诉编译器这一定是个常量。要用常量表达式初始化:
constexpr int a = 20;
constexpr int b = a + 1;
constexpr int size = Size(); // 只有constexpr函数这样声明才正确。
constexpr int Size() { return 10; } // constexpr函数。
- constexpr指针的初始化必须是nullptr或者0,或者是存于某个固定地址的对象。即对于函数体内一般是存非固定地址的,所以不能指向这样的变量。
- constexpr声明中如果定义了指针,限定符constexpr仅对指针有效,指针所指的对象无关:
const int *p = nullptr; // p是指针,指向一个整形常量。
constexpr int *q = nullptr; // p是常量指针,指向一个整形。
constexpr int i =123;
constexpr const int *p = &i; // p是常量指针,指向一个整形常量。注意这个 i 不能在函数体内定义。
2.5 处理类型
2.5.1 类型别名(type alias)
- 传统方法:
typedef double wages; // wages是double的同义词。
typedef wages base, *p; // base是double的同义词。p是double*的同义词。
- 别名声明:
using SI =Sales_item; // SI是Sales_item的同义词。
2.5.2 auto 类型说明符
auto sz = 0 , pi = 3.14 // 错误。sz和pi类型不一致。
- auto一般会忽略掉顶层const,同时底层const则会保留下来。
int i = 0;
const int ci = i, &cr = ci;
auto b = ci; // b 是整数(ci 顶层const被忽略)
auto c = cr; // c 是整数(cr 是 ci 别名,ci是顶层const)
auto d = &i; // d 是整形指针(指向i)
auto e = &ci; // e 是指针,指向一个常量(对常量对象区地址是一种底层const)
const auto f = ci; // f 是const int类型
auto &g = ci; // g 是 ci 的引用
2.5.3 decltype 类型指示符
- 用于选择并返回操作数的数据类型。
decltype(f()) sum = x; // sum类型就是 f() 函数的返回值类型,f()不会被调用。
- 1.decltype使用的表达式是变量时,返回该变量类型(包括顶层const和引用在内)。
const int a = 0, &b = c;
decltype(a) x = 1; // x 类型是 const int
decltype(b) y = 2; // y 类型是 const int&
decltype(b) z; // 错误:z类型是引用,必须初始化
- 2.decltype使用的表达式不是变量时,返回表达式结果的类型。
int i = 42, *p = &i, &r = i;
decltype(r + 2.3) b; // 正确:b 是浮点类型。
decltype(*p) c; // 错误:c 是 int&,必须初始化。这里*p是解引用,得到的是引用类型。而非int类型。
- 如果给变量加上括号,编译器就会把它当成一个表达式。即上面1情况变到2情况。
decltype((i)) d; // 错误:d 是 int& 类型,必须初始化。
decltype(i) e; // 正确:e 是 int 类型。
- auto 和 decltype的更多区别:这里