C++ Primer 学习笔记 PartI C++基础
Ch1开始
这一章包含控制流,条件,循环,注释,标准IO等内容。略。
Part I C++基础
CH2 变量和基本类型
2.1 基本内置类型
2.1.1 算术类型
2.1.1 类型转换
- 向unsigned赋超出范围的值,结果取余,对于signed,结果未定义。
- 不要混用signed和unsigned。表达式中既有signed又有unsigned,当signed为负值时会出现异常结果,因为它会自动转换为unsigned。
2.1.3 字面值常量
整型字面值:
- 默认情况下,十进制为带符号数(int, long, long long),八进制和十六进制则都有可能(前三种加上uint,ull)。
- 类型是能够容纳该数的有关类型中尺寸最小者,若超过所有类型的表示范围将发生错误。
- short没有对应的字面值。
- 十进制字面值中不含负号,负号的作用是取负值。
字符串字面值:
-
实质是常量字符构成的数组
-
编译器在末尾自动添加空字符'\0',因此实际长度比内容多1
-
转义序列
-
通过前后缀指定字面值类型
其他字面值: true false nullptr
2.2 变量
2.2.1 变量定义
列表初始化,使用花括号。列表初始化如果存在丢失信息的风险,则编译器会报错。
默认初始化:函数体外为0,函数体内为未定义。
2.2.2 变量定义与声明的关系
声明:使得名字为程序所知
定义:创建与名字关联的实体
分离式编译:详见2.6.3和6.1.3
extern int i; //声明
int j; //声明并定义
2.2.3 标识符
2.2.4 名字的作用域
2.3 复合类型
2.3.1 引用
引用即别名,本身不是一个对象。
定义时必须初始化,初始值不能是字面值,应该是对象,且引用类型与被引用对象的类型一致。
2.3.2 指针
指针本身是一个对象,也无须定义时初始化。
使用解引用符(*)来操作对象。
不能把int变量直接赋值给指针,即便值为0。
建议初始化所有指针。
void*指针
- 特殊指针类型,可存放任意对象的地址
- 只能与其他指针比较,作为函数的输入输出,或者赋值给另一个void*指针
- 不能直接操作void*指向的对象
指向指针的引用
int i = 42;
int *p;
int *&r = p; //从右向左阅读,r表示指向指针的引用
r = &i; //等价于p = &i
*r = 0; //等价于*p = 0
2.4 const 限定符
初始化和const
- 默认状态下,const对象尽在当前文件内有效
- 编译器在用到const变量处执行替换
- 如要在多个文件之间共享const对象,必须在定义前加extern关键字
2.4.1 const的引用
只能用对常量的引用来引用常量(引用的类型与所引用对象保持一致)
引用的类型与所引用对象保持一致,但存在例外情况:初始化常量引用时允许用任意表达式作为初始值,如字面值和非常量,只要该表达式能够转换成引用的类型即可。
int i = 42;
const int &r1 = i; //ok
const int &r2 = 42; //ok
const int &r3 = r1 * 2; //ok
int &r4 = r1 * 2; //wrong,非常量引用不能这么做
实质上,此时编译器创建了一个对应类型的临时量,该临时量被常量引用绑定。
常量引用仅对引用可参与的操作进行了限定,它可以引用非常量,但不能通过引用修改非常量的值。
2.4.2 指针和const
指针可以指向常量或非常量,指向常量的指针不能用于修改所指对象的值。
指针类型必须与其所指对象类型一直,但存在例外情况:允许一个指向常量的指针,指向一个非常量对象,与2.4.1的情况相似。总得来说,可以认为const类型的指针和引用自认为指向了常量,自觉地不去改变所指对象的值。
const指针
由于指针本身是一个对象,允许把指针本身定为常量。
int errNumb = 0;
int *const curErr = &errNumb; // 指向非常量的常量指针。可以通过curErr修改errNumb
const double pi = 3.14;
const double *const pip = π //指向常量的常量指针。
//从右向左阅读,表示pip是常量对象,且为指针。该指针指向的对象为const double。
2.4.3 顶层const
顶层const表示指针本身是常量(更一般的,表示任意对象本身是常量),底层const表示所指对象是常量(与指针、引用等复合类型的基本类型有关)。
当执行对象的拷贝操作时,顶层const通常不受影响,而底层const却不能忽视要求拷入和拷出对象具有相同的底层const资格,或者数据类型能够转换。一般来说,非常量可以转换成常量,反之则不行。
2.4.4 constexpr 和 常量表达式
常量表达式
指在编译期间就得到计算结果的表达式。
const int N = 114514; // yes
const int M = N + 1; // yes
int P = 998244353; // no,因为类型是非常量
const int sz = size(); //no,因为编译期无法获取
constexpr
C++ 11 规定,允许将变量声明为constexpr类型以由编译器验证变量的值是否为常量表达式。
constexpr const int sz = size(); //只有当size()是constexpr函数时才正确
6.5.2节将深入介绍constexpr函数。
如果你认定一个表达式是常量表达式,就声明为constexpr。
字面值类型
容易得到值的类型。如算术类型,引用,指针。
非字面值类型:自定义类,IO库,string类型等。
指针和引用能够定义为constexpr,但初始值受严格限制。
指针和constexpr
constexpr把它所定义的对象置为了顶层const,与指针所指的对象无关
2.5 处理类型
2.5.1 类型别名
传统方法 typedef
typedef double wages;
typedef wages base, *p; //base是double的别名,p是*double的别名
新标准 别名声明
using SI = Sales_item;
2.5.2 auto类型说明符
因为一条声明语句中只能有一个基本数据类型,因此该语句中所有变量初始基本数据类型必须一样。
auto i = 0, *p = &i; //ok,相当于int i = 0, *p = &i;
auto sz = 0, pi = 3.14; //wrong
auto一般会忽略顶层const,而底层const被保留
const int ci = i, &cr = ci;
auto b = ci; //b为int
auto c = cr; //c为int
auto d = &i; //d为int*
auto e = &ci; //e为(const int)*
如果希望推断出的auto类型是顶层const,需要明确指出
const auto f = ci; //ci的推演类型为int,f为const int
还可以将引用类型设置为auto,这时原来的初始化规则仍适用
auto &g = ci; //ok
auto &h = 42; //wrong, 非常量引用不能绑定字面值
const auto &j = 42; //ok,常量引用可以绑定字面值
2.5.3 decltype类型指示符
对于需要根据表达式推断类型,而不用该表达式初始化变量的情况,C++11引入了第二种类型说明符decltype
decltype(f()) sum = x; //sum的类型为f的返回类型
const int ci = 0, &cj = ci;
decltype(ci) x = 0; // const int x = 0;
decltype(cj) y = x; // const int &y = x;
decltype(cj) y; //错误,引用必须初始化
decltype与引用
有些时候表达式将向decltype返回引用。这种表达式通常可以作为一条复制语句的左值。
int i = 42, *p = &i, &r = i;
decltype(r + 0) b; //b是未初始化的int,因为r+0的结果是int型值
decltype(*p) c; //错误,c是int&,必须初始化
如*p, 表达式的内容是解引用操作,decltype的结果为int&。
与auto不同,decltype的结果形式与表达式形式密切相关,例如,当表达式被括号括起时,decltype的结果将为引用。
decltype((i)) d; //错误,d是int&,必须初始化
decltype(i) e; //正确,e是未初始化的int。
decltype((表达式))的结果永远为引用。当且仅当表达式结果为引用时,decltype(表达式)的结果为引用。
2.6 自定义数据结构
略。
预处理器概述
预处理器看到#include 标记时,用指定头文件的内容代替#include
此外,还有头文件保护符:
'#define' 把一个名字设置为预处理变量
'#ifdef' 当且仅当变量已定义时为真
'#ifndef' 当且仅当变量为定义时为真
一旦检查结果为真,则执行后续操作,直至遇到#endif为止。
使用这些功能能够有效防止重复包含的情况发生,因此最好习惯性地设置保护符,不管该文件是否被其他文件包含。
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string>
struct Sales_data {
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
}
#endif
预处理变量无视C++语言中关于作用域的规则。
CH3 字符串,向量和数组
因为这部分内容在算法竞赛中较为常见,仅记录不太熟悉的内容以及编码提示等。
3.1 using
头文件不应该包含using声明,因为所有引用它的文件都会受此影响,可能产生冲突。
3.2 string
由于历史原因,字符串字面值不是string类对象,使用string类加法时需确保加号两侧至少有一个是string对象。
3.3 vector
vector是一种类模板。
range for语句体内不应改变其遍历序列的大小。
3.4 迭代器
*iter
返回iter的引用
iter->num
等价于(*iter).num
++ / -- iter
令iter指示下一个/上一个元素
iter1 ==/!= iter2
判断是否指向同一个元素,或指向同一个容器的尾后迭代器
迭代器类型
iterator和const_iterator。如vector<int>::const_iterator
常量迭代器类似常量指针,只能读不能写。如果对象类型为常量,只能使用常量迭代器,否则都可以。
凡是使用迭代器的循环体,都不应向迭代器所属容器中添加元素。
迭代器运算
与整数加减,仍得到迭代器。
迭代器加减,得到距离。类型是名为difference_type的带符号整数。
迭代器比较,前者小于后者。
使用迭代器进行二分查找
auto beg = v.begin(), end = v.end();
auto mid = beg + (end - beg) / 2;
while (mid != end && *mid != target) {
if (target < *mid) end = mid;
else beg = mid + 1;
mid = beg + (end - beg) / 2;
}
3.5 数组
int *(&arry)[10] = ptrs;
//从内向外阅读:arry是一个数组的引用,该数组含有10个int型指针
该部分内容基本与C语言相同
CH4 表达式
待填
CH5 语句
待填
upd:
弃坑,并提供一条迅速掌握c++通过考试并可以写点东西的学习路径:
1.学习https://www.bilibili.com/video/BV1ob411q7vb 并完成http://cxsjsxmooc.openjudge.cn/2023t3spring/ 习题
2.学习https://www.bilibili.com/video/BV1N34y1H7x7
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律