前言
之前笔试了几家公司,居然在语言这种基础的问题上摔跟头。不得不进行恶补。科研任务重,C++ primer不能丢。因此,准备一波C++ primer深入阅读计划。
C++ primer 主要分为四个大的部分:
- C++基础
- C++标准库
- 类设计者的工具
- 高级主题
这个系列的复习将从C++基础开始,着重复习自己之前忘记或者不清楚的地方,C++11新的标准也加入了一些新的语法,需要熟练掌握,力求深入分析。此后准备阅读侯捷大神的《深入探索C++对象模型》这本书。路漫漫其修远兮,吾将上下而求索。
变量和基本类型
2.1基本内置类型
包括算数类型和空类型(void)。
算数类型包括:
字符、整数、浮点数、布尔值
.字符类型根据尺寸大小,分为宽字符、普通字符,普通字符(char)8位,宽字符有多种。
.整数类型如下:
类型 | 含义 | 最小尺寸 |
---|---|---|
short | 短整形 | 16位 |
int | 整形 | 32位 |
long long | 长整型 | 64位 |
- .浮点类型如下:
类型 | 含义 | 最小尺寸 |
---|---|---|
float | 单精度 | 6位有效数字 |
double | 双精度 | 10位有效数字 |
long double | 扩展精度 | 10位有效数字 |
整形又可以分为带符号数(signed)、无符号数(unsigned)
它们的区别在于带符号数需要使用最高位作为符号位,而无符号数不用,因此可以推断
signed int范围是 −231→231−1 , 最大是2147483647
unsigned int范围是 0→232−1 ,一般的,涉及到位运算是,因为有右移运算符,所以最好使用unsigned类型
字面值常量
一个型如42的值就被称作字面值常量,注意,它是一个常量。另外,它也有类型,根据形式和值决定了它的数据类型。
举例:
20 /* 十进制*/; 024 /* 八进制*/; 0x14 /* 十六进制 */
3.1415; 3.1415E0; 0.; 0e0; .001;
‘a’ // 字符字面值
“Hello world” // 字符串字面值, 需要提一下的是,字符串后面的空字符’\0’, 不要忘了
转义序列
最后我们可以指定字面值的类型,比如 L’a’ // 宽字符
1E-3F //单精度
2.2 变量
最重要的是初始化,初始化和赋值有区别:
初始化指的是对象在创建时的获得的一个值; 而赋值是把对象当前的值擦除,然后用新值替代。然而这个区别有啥用,暂时没用。直接上例子:
int units_sold = 0;
int units_sold = {0}; // 列表初始化
int units_sold{0};
int units_sold(0); //不是一个函数声明
需要注意的是,列表初始化执行隐式转换时,如果存在丢失信息的危险,转换未执行。
1. 默认初始化
重点来了。如果变量定义时没有指定初值,则变量执行默认初始化。
- 定义在任何函数之外的内置类型,默认初始化为0
- 定义在函数体内部的内置类型,将不被初始化(是不确定的值)
- 类类型各自决定其初始化对象的方式。
2. 变量声明和定义
C++支持分离式编译,即可以将程序分成若干文件,每个文件独立编译。那文件之间需要共享代码和变量。因此就需要声明和定义。
声明(类型+名字):使名字为程序所知,要使用名字必须要对名字进行声明。使用 extern 关键字
定义(类型+名字+初始化):创建与名字相关联的实体。
例子:
extern int i; // 声明i,i在别处定义
int j; // 定义
extern double pi = 3.1415; // 定义而非声明,因为有显示初始化
注意:变量只能定义一次,但可以被声明多次。
2.3复合类型
复合类型有几种,此处介绍两种:引用、指针。
1.引用
引用可以理解成,为对象起了另外一个名字。那么,引用本身并非对象,它只是为已经存在的对象起另外一个名字。因此,所有围绕引用的操作都是在它绑定的对象上操作。
int ival = 1024;
int &refVal = ival; // 注意,引用必须初始化
2.指针
指针是一个对象,保存的是另一个对象的地址,因此实现了对其他对象的间接访问。指针不需要定义时赋初值。
int ival = 42;
int *p = &ival; // &在这里是取地址
*p = 1023;
double *a; // 可以不初始化
int *p1 = nullptr; // 空指针,养成习惯,每次定义指针时设置成初值,或者空指针
int *p2 = 0; // 也是空指针
int *p3 = NULL; // 同上,NULL是预处理变量(编译开始阶段),在cstdlib头文件中定义
int zero = 0;
p = zero; // 错误,这里是赋值,不能把int变量给一个指针类型
void *pv = &obj; // void* 指针可以存放任意对象的地址
int *&a = p; // a 是指针p 的一个引用
2.4 const限定符
const变量是常量,程序里无法改变这个值,于是方便对一个常量进行调整,而不用担心程序运行过程会改变它的值。
const对象一旦创建就无法修改
const int i = get_size(); // 运行时初始化
const int j = 3; // 编译时初始化
const int k; // 错误, k未经过初始化,必须初始化
默认情况下,const对象仅在文件内有效,多个文件中存在同名的const对象时,它们是相互独立的。
如果要在多个文件中共享const对象,那不论是定义还是声明都要加上extern关键字
//a file
extern const int i = fcn();
//b file
extern const int i;
1.const的引用
把引用绑定到const对象上,就叫对常量的引用。不能通过常量引用修改绑定的对象。另外,非常量引用不能绑定到常量对像,但是常量引用可以绑定常量对象,也可以绑定非常量对象。
int i = 42;
const int &r1 = i; // 正确,允许const引用绑定到普通对象
const int &r2 = 42; // 字面值常量
const int &r3 = r1 * 2; // 正确,建立了一个临时常量
int &r4 = r1 * 2; // 错误,r4是一个非常量引用,不能绑定常量对象
i ++; // r1 随i变化,但是不能通过 r1 改变指向的对象
double a = 3.14;
const int &ri = a;
a = 4.13; // 这里ri指向的是一个临时变量,修改a,ri指向的值不变
2.指针和const
两种情况
指针指向一个常量对象(指向常量的指针)
const int i = 0;
const int *p = &i;
int j = 1;
p = &j; // 正确,可以对指针进行赋值,但是不能通过指针修改指向的对象
常量指针,即指针本身是一个常量(常量指针)
int i = 0;
int *const p = &i;
const double j = 3.12;
const double *const p1 = &j;
int h = 1;
p = &h; // 错误,常量指针不能进行赋值,但是可以通过指针修改指向的对象
3.顶层const,底层const
其实,顶层的意思就是对象本身是一个常量。底层的意思是引用或者指针指向或绑定的对象是一个常量
记住一句话
常量量指针或常量引用可以指向常量对象,也可以指向非常量(注意临时对象)
指向非常量的指针或者绑定非常量的引用不能指向或绑定常量对象
4.constexpr 和 常量表达式
感觉用处不大
2.5 处理类型
1. 类型别名
- typedef 关键字
typedef Sales_item SI; // 类类型
typedef long long LL; // 长整形
typedef int *const const_point; // 指针
typedef int (*func_point)(int a, int b); // 函数指针
- using 关键字
using SI = Sales_item;
using LL = long long;
using const_point = int *const;
using func_point = int(*)(int, int);
注意:
const + 类型别名时,const 修饰的是类型本身,不是复合类型指向的对象
typedef char *pstring;
const pstring cstr = 0; // 指向char 的常量指针
const pstring *ps; // ps是一个指针,它的对象是指向char 的常量指针
// 注意,这里不等于const char *cstr
2. auto 类型说明符
auto关键字的意思是,编译器分析变量类型,而不用显示说明变量类型。但是,auto修饰的变量必须要初始化。
vector<int> arr;
for (auto element : arr) {
cout << element << endl;
}
3. decltype 类型指示符
场景是,想使用某个表达式推断出要定义的类型,但是并不想用该表达式的值初始化变量。
decltype(f()) sum = x; // sum 是f函数的返回类型
int i = 10;
decltype((i)) d; // 错误,括号里面是一个表达式,d是 int &,必须要初始化
decltype(i) e; // 正确,括号里面是一个变量,d是 int
注意:
decltype((varible))的结果永远是引用
decltype(varible)的结果根据varible的类型来判断