《C++ Primer》第五版学习笔记
第一章 开始
1.1 编写一个简单的c++程序
C++程序必须包含一个main函数:
int main()
{
return 0;
}
以左花括号开始, 右花括号结束,每一条语句以分号结束。
1.1.1 编译、运行环境
常见的文件名后缀有.cc\.cxx\.cpp\.cp\.c
从命令行运行编译器
假设main程序保存在prog1.cc中。用如下指令编译:
$ CC prog1.cc
CC是编译器的名字。编译结果会生成一个可执行文件prog1.exe。
1.2 初识输入输出
iostream | 输入流、输出流标准库 |
---|---|
cin | istream类型对象,标准输入(standard input) |
cout | ostream类型对象,标准输出(standard output) |
cerr | 用来输出警告和错误信息 |
clog | 输出程序运行时的日志 |
调用:
#include <iostream>
使用, >>是输入运算符,<<是输出运算符, std::指出cout和endl是定义在std的命名空间上的。
std::cout << "Enter two numbers:" << std::endl;
std::cin >> x >> y;
1.3 注释简介
- /*…*/(不能嵌套)
- //
1.4 控制流
1.4.1、1.4.2 while, for语句
语法(for同样适用):
#include <iostream>
int main()
{
while (条件) {
循环体;
}
return 0;
}
1.4.3 读取数量不定的输入数据
while (std::cin >> 自定义变量value)
1.4.4 if语句
语法:
if (条件) {
执行语句;
}
else {
执行语句;
}
1.5 类(class)简介
1.5.1 Sales_item类
-
用途:用过定义一个类来定义自己的数据结构。
-
需要使用头文件来访问自己定义的类。通常用.h为头文件后缀
例:
#include <iostream> #include "Sales_item.h" int main() { Sales_item book; //定义一个book对象,保存从标准输入读取的数据 std::cin >> book; //读入参数book(比如isbn号) std::cout << book << std::endl; return 0; }
1.5.2 成员函数
成员函数就是定义在类中的函数,比如book.isbn()
第二章 变量和基本类型
2.1 基本内置类型
(此部分和c相同,不再概述)
2.2 变量定义
int x, y;
Sales_item item; //item的类型是Sales_item,参见1.5.1
std::string book("好他娘的困啊"); //string是一种库类型,表示可变长的字符序列
拓展阅读
什么是对象(object)?
对象是指一块能存储数据并具有某种类型的内存空间。
声明
如果要在多个文件中使用同一个变量,必须将声明和定义分离。变量的定义只能出现在一个文件中,其他用到该变量的文件中必须对其声明,不能重复定义。
extern int i;
2.3 复合类型
2.3.1 引用
int ival = 1024;
int &refVal = ival; //refVal指向ival,是ival的另一个名字
引用的类型需要相同,比如都是int类型或者都为double类型等。
2.3.2 指针
指针同引用一样, 也实现了对其他对象的间接访问。但是,和引用不同的是,指针本身就是一个对象,可以对其赋值和拷贝。
int *ip1, *ip2;
int val = 42;
int *p = &val; // p存放变量val的地址,p是指向变量val的指针
cout << *p; //由符号*得到指针p所指对象,输出42
int *p1 = nullptr; //空指针
/*
赋值永远改变的是等号左侧的对象
*/
int i = 42;
int *pi = 0; // pi初始化,但没有指向任何对象
int *pi2 = &i; // pi初始化,存有i的地址
pi3 = pi2; // pi3和pi2指向同一个对象i
2.3.3 理解复合类型的声明
指向指针的指针
int **ppi = π
2.4 const限定符
const定义变量,其值不会改变,定义时必须初始化。
const int bufSize = 512;
2.4.1 const的引用
把引用绑定到const对象上,称之为对常量的引用。
int i = 42;
const int &r1 = i; // 允许将const int绑定到一个普通的int对象
const int &r3 = r1 * 2; // ✔:r3是一个常量引用
int &r4 = r1 * 2; // ❌,r4是非常量引用
2.4.2 指针和const
指向常量的指针
const double pi = 3.14;
double *ptr = π // ❌,ptr是一个普通的指针
const double *ctpr = π //✔
*cptr = 42; // ❌,不能给*ctpr赋值,它是const类型的
const指针
const double pi = 3.14;
const double *const pip = &pi // pip是指向常量对象的常量指针,一直指向pi
2.4.3 顶层const
顶层表示指针本身是一个常量,底层表示指针所指的对象是一个常量。
int i = 0;
int *const p1 = &i; // 不能改变p1的值,这是一个顶层const
const int ci = 42; // 不能改变ci的值,这是一个顶层const
const int *p2 = &ci; //可以改变p2的值,这是一个底层const
底层const的限制:拷入和拷出对象必须同为底层const:
/*接上一段代码*/
int *p = p3; // ❌,p3包含底层const定义,p没有
p2 = p3; // ✔,均为底层const
p2 = &i; // ✔,int*可以转换成const int*
2.4.4 constexpr和常量表达式
const int wayne = 20; // wayne是常量表达式
int del = 19; //del不是常量表达式
声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化。
const int *p = nullptr; // p是一个指向整型常量的指针
constexpr int *q = nullptr; // q是一个指向整数的常量指针
constexpr把它所定义的对象置为了顶层const。
2.5 处理类型
2.5.1 类型别名
即定义类型的同义词:
typedef double wayne; // wayne是double的同义词
typedef wayne del, *p; // del是double的同义词,p是double*的同义词
wayne hourly, weekly; //等价于double hourly, weekly
别名声明:
using SI = Sales_item; // SI是Sales_item的同义词
SI item; //等价于 Sales_item item
2.5.2 auto类型说明符
auto类型说明符能让编译其替我们分析表达式所属的类型。auto的定义必须有初始值:
auto i = 0, *p = &i; //✔,i是整数,p是整型地址
auto sz = 0, pi = 3.14; // ❌,sz和pi类型不一样
2.5.3 decltype类型指示符
decltype的作用是选择并返回操作数的数据类型。
decltype (f()) sum = x; // sum的类型就是函数f的返回类型
2.6 自定义数据结构
2.6.3 编写自己的头文件
类通常被定义在头文件中,而且类所在头文件的名字应该与类名一样。
预处理器
#define指令把一个名字设定为预处理变量,另外两个指令分别检查摸个指定的预处理变量是否已经定义:#ifdef当且仅当变量已定义时为真,#ifndef当且仅当变量未定义时为真。一旦检查结果为真,则执行后续操作直至遇到#endif指令为止。
第三章 字符串、向量和数组
3.1 命名空间的using声明
之前介绍的库函数都属于命名空间std,std::cin表示从标准输入中读取内容。作用域操作符(::)的含义是编译器应从操作符左侧名字所示的作用域中寻找右侧那个名字。因此,std::cin的意思就使要使用命名空间std中的名字cin。
该方法略繁琐,使用using声明更简单,格式:
using namespace::name;
一旦声明上述语句,可以直接访问命名空间中的名字。
#include <iostream>
using std::cin;
int main()
{
int i;
cin >> i; // 正确
cout << i; // ❌, 没有using声明
std::cout << i; //正确,显示从std中使用cout
return 0;
}
3.2 标准库类型string
使用string类型必须包含string头文件。string定义在命名空间std中:
#include <string>
using std::string;
对string对象的一些操作:
- getline(is,s) 从is中读取一行赋给s
- s.empty() 判断字符是否为空,返回布尔值
- s.size() 返回字符个数
- s[n] 字符串索引
读取未知数量的string对象
int main()
{
string word;
while (cin>> word) // 反复读取,直至到达文件末尾
cout << word << endl; // 逐个输出单词,每个单词后紧跟一个换行
return 0;
}
使用getline读取一整行
有时我们希望能在最终得到的字符串中保留输入时的空白符,这时应该用getline函数代替原来的>>运算符。getline只要一遇到换行符就结束读取操作并返回结果。
处理每个字符
使用基于范围的for语句可以对string对象中的每一个字符操作。语法:
for (declaration: expression)
statement
expression是一个对象,用于表示一个序列。declaration负责定义一个变量,该变量用于访问序列中的基础元素。
string str("some string");
for (auto c : str) // 对于str中的每个字符
cout << c << endl; // 输出当前字符,后面紧跟一个换行符
使用for改变字符串中的字符
string s("Hello World!!!");
for (auto &c:s) // 对于s中的每一个字符(注意:c是引用)
c = toupper(c); // c是一个引用,因此赋值语句将改变s中字符的值
cout << s << endl;
3.3 标准库类型vector
vector表示对象的集合,其中所有对象的类型都相同。使用vector需要声明:
# include <vector>
using std::vector;
3.4 迭代器
(没有什么好讲的)
3.5 数组
在数组的声明中,维度必须是一个常量表达式(由const、constexpr定义)
理解复杂的数组声明
可以定义一个存放指针的数组。又因为数组本身就是对象,所以允许定义数组的指针及数组的引用。
int *ptrs[10]; // ptrs是含有10个整型指针的数组
int &refs[10] = /*?*/; // ❌ 不存在引用的数组
int (*parray)[10] = &arr; // parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr; // arrRef引用一个含有10个整数的数组
对于parray可以由内向外阅读:首先是圆括号括起来的部分,*parray意味着它是一个指针,接下来观察右边,可以知道parray是一个指向大小为10的数组的指针,最后观察左边知道数组中的元素是int。
在使用数组下标时,通常将其定义为size_t类型。size_t时机器相关的无符号类型
指针和数组
使用取地址符来获取指向某个对象的指针。数组的元素也是对象,对数组使用下标运算得到该数组指定位置的元素。因此向其他对象一样,对数组的元素使用取地址符就能得到指向该元素的指针:
string nums = {"I", "wanna", "die"}; // 数组元素是string对象
string *p = &nums[0]; // p指向nums的第一个元素
string *p2 = nums; // 等价于p2 = &nums[0]
指针也是迭代器
int arr[] = {0,1,2,3,4,5};
int *p = arr; // p指向arr的第一个元素
++p; // p指向arr[1]S
标准库函数begin和end
int ia[] = {0,1,2,3,4,5};
int *beg = begin(ia); // 指向ia首元素的指针
int *last = end(ia); // 指向ia尾元素的下一位置的指针
第四章 表达式、第五章 语句
(和c相同)
第六章 函数
如果同一作用域内的几个函数名字相同但形参列表不同,我们称之为重载函数。比如print函数
------(基础学完啦)--------