来自于《c++ primer》
Update: 发现看书好累QwQ,找了sjtu.ji的课件放在了文末
第二章 变量和基本类型
- 如何选择类型:
- 使用int执行整数运算,如果你的数值超过了int表示的范围,选用long long
- 在算数表达式中不要使用char或bool
- 执行浮点数运算选用double,这是因为float通常精度不够而且双精度浮点数和单精度浮点数的计算代价相差无几。long double提供的精度在通常情况下是没有必要的。
当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。
当我们赋给带符号类型一个超出它表示范围发值时,结果是未定义的,程序可能继续工作,可能崩溃,可能生成垃圾数据。当一个算数表达式中既有无符号数又有int值时,那个int值就会转换成无符号数。
1
2
3unsigned u = 10;
int i = -42
std::cout << u + i << std::endl; //如果int占32位,输出4294967264字符串字面值的类型实际上是由常量字符构成的数组,编译器在每个字符串的结尾处添加一个空字符
('\0')初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值替代。
可以用花括号初始化变量
1
2
3int a = 0;
int a = {0};
int a{0}声明使得名字为程序所知,定义负责创建与名字关联的实体。声明规定了变量的类型和名字,这一点与定义相同。但是除此之外,定义还申请存储空间,也可能会为变量赋一个初始值。如果想声明一个变量而非定义它,加
extern1
extern int i;
用户自定义的类名一般以大写字母开头,如Sales_item
全局作用域没有名字
引用必须被初始化,程序把引用和它的初始值绑定在一起,而不是将初始值拷贝给引用。因为引用本身不是对象,所以不能定义引用的引用。同理不能定义指向引用的指针
指针最好都初始化成nullptr。任何非0指针对应的条件值都是true
void*指针可用于存放任意对象的地址。使用它是因为我们对该地址中到底是个什么类型的对象并不了解
定义多个变量时:
1
int* p1, p2 //p1是指向int的指针,p2是int
存在对指针的引用:
1
2
3
4
5int i = 42;
int *p = nullptr;
int *&r = p; //从左到右阅读r理解含义
r = &i;
*r = 0const对象必须初始化,编译器在编译过程中把用到该变量的地方都换成对应的值。const对象仅在该文件内有效
对常量的引用:
1
2const int c1 = 1024;
const int &rl = ci;当一个常量引用被绑定到另一种类型上时:
1
2
3
4
5double dval = 3.14;
const int &ri = dval;
//实际:
//const int temp = dval; //由双精度浮点数生成一个临时的整形变量
//const int &ri = temp; //让ri绑定这个临时量当ri不是常量时,这种行为非法,否则就相当于对一个临时量赋值
指向常量的指针:
1
2const double pi = 3.14;
const double *cptr = π但是允许令一个指向常量的指针指向一个非常量对象,所谓指向常量的指针仅仅要求不能通过该指针改变对象的值
常量指针:
1
2
3
4int errNumb = 0;
int *const curErr = &errNumb; //curErr将一直指向errNumb
const double pi = 3.14;
const double *const pip = π //pip是一个指向常量对象的常量指针顶层、底层const
对于指针:顶层const表示指针本身是一个常量,底层const表示指针所指的对象是一个常量1
2
3
4
5int i = 0;
int *const p1 = &i; //顶层
const int ci = 42; //顶层
const int *p2 = &ci; //底层
const int &r = ci; //底层,类似于指向常量的指针c++并不允许随意改变引用所绑定的对象
constexpr
https://stackoverflow.com/questions/14116003/difference-between-constexpr-and-const
constexpr修饰的函数,简单的来说,如果其传入的参数可以在编译时期计算出来,那么这个函数就会产生编译时期的值。但是,传入的参数如果不能在编译时期计算出来,那么constexpr修饰的函数就和普通函数一样了。不过,我们不必因此而写两个版本,所以如果函数体适用于constexpr函数的条件,可以尽量加上constexpr。
constexpr声明如果定义了一个指针,限定符constexpr仅对指针有效。constexpr把它所定义的对象置为了顶层const
类型别名
1
2typedef double wages;
using wages = doouble;auto
auto一般会忽略调顶层const,而底层const会保留下来1
2
3
4const int ci = 1;
auto b = ci; //b是一个整数
const auto f = ci; //f是const int
auto e = &ci; //e是一个指向整数常量的指针(对常量对象取地址是一种底层const)decltype
作用是选择并返回操作数的数据类型1
decltype(f()) sum = x;
如果表达式是一个变量,则返回该变量的类型
如果表达式是一个数组,则返回数组类型
如果表达式的内容是解引用操作,则返回引用类型1
2
3
4int i = 42, *p = &i, &r = i;
decltype(r + 0) b; //int类型
decltype(*p) c //错误,c是int&,必须初始化decltype((variable))的结果永远是引用,而decltype(variable)结果只有当variable本身就是一个引用时才是引用
第三章 字符串、向量和数组
string表示可变长的字符序列1
2
using std::string;初始化string对象的方式
1
2
3
4
5
6string s1;
string s2(s1);
string s2 = s1;
string s3("value"); //不包含字面值最后的空字符串
string s3 = "value";
string s4(n,'c'); //把s4初始化为由连续n个字符c组成的串
3.读取string对象
1 | int main() |
在执行读取操作时,string对象会自动忽略开头的空白(空格符、换行符、制表符等),并从第一个真正的字符开始读起,直到遇到下一处空白为止。
getline函数
参数为输入流和一个string对象getline(cin,string line)
函数从给定的输入流中读取内容,直到遇到换行符为止(注意换行符也被读进来了),然后把所读的内容存入到那个string对象中去(注意不存换行符)string的empty和size操作
empty为string的一个成员函数,根据对象是否为空返回一个对应的bool值
size也为string的一个成员函数,返回string对象的长度
size的返回类型是string::size_type,是一个无符号整型数。可以使用auto方便处理
1 | auto len = line.size(); |
string 类型之间可以直接相加+,也可以至少一个string类型与字符字面值相加
处理string对象中的字符
cctype头文件的函数:1
2
3isalnum(),isalpha(),isdigit(),islower(),ispunct()//当为标点符号为真
isspace() //非空格(0x20,''),換頁(0x0c,'\f'),換行(0x0a,'\n'),回車(0x0d,'\r'),水平製表符(0x09,'\t'),垂直標簽(0x0b,'\v')
isupper(),tolower(),toupper()范围for语句
1
2
3
4
5
6
7string s("Hello world");
decltype(s.size()) punct_cnt = 0;
for (auto c : s)
if (ispunct(c))
++punct_cnt;
cout << punct_cnt
<< "punctuation characters in " << s << endl;如果想改变string对象中字符的值,必须把循环变量定义成引用类型
for (auto &c : s)初始化vector对象(部分)
1
2
3
4
5vector<int> v1(10); //v1有10个元素,每一个都是0
vector<int> v2{10}; //一个元素,值为10
vector<int> v3(10,1); //有10个元素,每一个都是1
vector<int> v4{10,1}; //有2个元素,值为10和1push_back()
使用v1.push_back(i)将int i 放到v1尾端添加元素
vector以及string对象的下标运算符可用于访问已存在的字符,但是不能用于添加字符迭代器
v.begin()返回指向第一个元素的迭代器,v.end()返回尾元素的下一个位置的迭代器1
2
3
4
5string s("some string");
if (s.begin() != s.end()) { //确保s非空
auto it = s.begin();
*it = toupper(*it);
}*iter返回迭代器iter所指元素的引用iter->m等价于(iter).m迭代器类型
一般用auto1
2
3
4
5
6
7vector<int>::iterator it;
string::iterator it2;
vector<int>::const_iterator it3; //只读不写
string::const_iterator it4;
vector<int> v;
auto it5 = v.cbegin() //it5的类型是`vector<int>::const_iterator`复杂数组的声明
1
2
3
4int *ptrs[10]; //含有10个整型类型的数组
int (*Parray)[10] = &arr; //Parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr; //arrRef引用一个含有10个整数的数组
int *(&arry)[10] = arr1; //arry是数组的引用,该数组有10个指针指针与数组
1
2
3
4
5
6int ia[] = {0,1,2,3};
auto ia2(ia) //ia2是一个整型指针,指向ia的第一个元素
decltype(ia) ia3 = {4,5,6,7} //正确
int *beg = begin(ia);
int *last = end(ia);允许使用数组来初始化vector对象
1
2int int_arr[] = {0,1,2,3};
vector<int> ivec(begin(int_arr), end(int_arr));find()
1
2
3
4
5
6
7
if ( std::find(vec.begin(), vec.end(), item) != vec.end() )
do_this();
else
do_that();
第四章 表达式
在运算类型转换当中,小整数类型(bool,char,short等)通常会被提升成较大的整数类型,主要是int
rvalue and lvalue
当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)在除法运算中,商一律向0取整(直接切除小数部分)
短路求值
逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值如果赋值运算符的左右两个运算对象类型不同,则右侧运算对象将转换成左侧运算对象的类型
赋值运算符满足右结合律
1
2int ival,jval;
ival = jval = 0; //先运算jval = 0,再ival = jval条件运算符
1
2cout << ((grade < 60) ? "fail : "pass"); //输出fail或者pass
cout << (grade < 60) ? "fail : "pass"; //输出1或者0移位运算符
左移运算符(<<)在右侧插入值为0的二进制位。右移运算符(>>)的行为则依赖于其左侧运算对象的类型:如果该运算对象是无符号类型,在左侧插入值为0的二进制位;如果该运算对象是带符号类型,在左侧插入符号位的副本或值为0的二进制位,如何选择视具体情况而定。
强烈建议将位运算符用于处理无符号类型
第五章 语句
switch语句
case标签必须是整型常量表达式try-catch,throw
暂无(?)
第六章 函数
函数对形参做的所有操作都不会影响实参
如果函数无需改变引用形参的值,最好将其声明为常量引用
当用实参初始化形参的时候会忽略顶层const
1
2void fcn(const int i) {/* fcn能够读取i,但是不能向i写值 */}
void fcn(int i) {/* ... */} //错误:重复定义了fcn(int)调用fcn函数时,既可以传入const int 也可以传入int
我们可以使用非常量初始化一个底层const对象,但是反过来不行
1
2
3int i = 42;
const int *cp = &i; //正确,cp不能改变i
int *p = cp; //错误,p的类型和cp的类型不匹配为函数传递一个数组时,实际上传递的是指向数组首元素的指针
下面三个print函数是等价的:1
2
3void print(const int*);
void print(const int[]);
void print(const int[10]);数组的大小对函数的调用没有影响
列表初始化返回值
1
2
3
4
5vector<string> process()
{
// ...
return {"functionX","ok"}; //返回列表初始化的vector对象
}声明一个返回数组指针的函数
type (*fuction(parameter_list)) [dimension]
如int (*func(int i)) [10];
或者auto func(int i) -> int(*)[10;]函数重载
不允许两个函数除了返回类型以外其他所有要素相同,函数重载时形参列表应该有明显的区别const_cast和重载
1
2
3
4
5
6
7
8
9
10
11const string &shorterString(const string &s1, const string &s2)
{
return s1.size() <= s2.size() ? s1: s2;
}
string &shorterString(string &s1, string &s2)
{
auto &r = sshorterString(const_cast<const string&>(s1),
const_cast<const string&>(s2));
return const_cast<string&>(r);
}constexpr 函数
函数的返回类型及所有形参的类型都得是字面量类型,而且函数体中必须有且只有一条return语句。编译器把对constexpr函数的调用替换成其结果值assert与NDEBUG
看书 p215函数指针
1
2
3
4
5
6
7
8
9
10
11
12bool lengthCompare(const string &, const string &);
// pf指向一个函数,该函数的参数是两个const string的引用,返回值是bool类型
bool (*pf) (const string &, const string &);
//下面的赋值语句相同
pf = lengthComare;
pf = &lengthCompare;
//下面的调用语句相同
bool b1 = pf("hello","goodbye");
bool b2 = (*pf) ("hello","goodbye");
bool b3 = lengthCompare("hello","goodbye");声明返回指向函数类型的指针
int (*f1(int)) (int*,int);auto f1(int) -> int(*)(int*,int);
(int*,int)是指针的形参列表
课件
仅供参考