C++ Primer Plus 读书感悟
1. vector的扩容机制
总结:0~4都是增加一个扩容一个元素,4以后当容量不够时扩容至1.5倍 (这个结论仅限VS下)
如果使用GCC的话会发现扩容的倍数将会是2倍。
2. 智能指针
-
auto_ptr 和 unique_ptr 是所有权的归属发生了转换,share_ptr 是在内部进行了一个引用计数,当计数等于0时才调用delete
-
智能指针的定义
void printInt(int num) { int* a = new int(num); //*a = num; printf("%p, %d\n", a, *a); shared_ptr<int> ps; ps = shared_ptr<int>(a); printf("%p, %d\n", ps, *ps); cout.operator<< (*ps) << endl; } void printString(string str) { shared_ptr<string> ps(new string("hello world")); cout << *ps << endl; printf("%s\n", ps->c_str()); }
注意:打印智能指针的解引用的值时一定要使用cout,因为printf是不可以打印一个类的值的。
-
unique_ptr 有自己的保护机制,如果使用一个指针直接用=号给另一个指针赋值时,此时是不允许的,因为会发生悬挂指针的问题,原有的指针会发生因为所有权的转移而无法使用的问题。那么如果想进行赋值的话,要使用C++11的
move()
函数unique_ptr<string> ps1, ps2; ps1 = unique_ptr<string> (new string("hello world")); //ps2 = ps1; //不允许 ps2 = move(ps1);
使用move() 函数后,此时这两个指针都可以访问到正确的结果。
-
如何选择智能指针
-
如果程序要使用多个指向同一个对象的指针,使用share_ptr
-
如果不需要多个指针指向同一个对象,可以使用unique_ptr,当一个函数使用new分配内存,那么它的返回值可以使用unique_ptr来进行返回值的接收,同时可以将unique_ptr变为该地址的所有者,并管理内存的释放。
unique_ptr<int> make_int(int num) { return unique_ptr<int>(new int(num)); }
-
3. STL中算法的使用
next_permutation(xxx.begin(), endxxx.end)
,用于获得全排列的下一种排列方式
4. 文件操作(流)
-
读文件使用 ifstream
-
写文件使用 ofstream
-
fstream继承与以上两个类,可以同时进行读和写的操作
fstream fins("abc.txt", ios_base::app | ios::in || ios::out); char ch; while (fins.read((char*)&ch, sizeof ch)) { cout << ch; } cout << endl;
以上代码使用了
.read((char*)&ch, sizeof ch)
的方法进行了文件内容的读出
5. 字符串流
-
istringstream 用来输出字符串,它的定义必须要指定初始化的字符串,输出后的字符串不包含空格
-
ostringstream 用来输入字符串,当输入打算结束时,调用 .str() 方法可以返回其中的所有字符串
-
stringstream 可以同时输入和输出字符串,特点是也没有空格。
// ostringstream 用来向其中输入字符串,并通过.str()方法进行返回一个字符串 ostringstream ostr; ostr << "hello world!"; ostr << "alin"; ostr << " loves c++!"; cout << ostr.str() << endl; /*ostr << "abcdef"; cout << ostr.str() << endl;*/ // istringstream 用来输出字符串,在定义时要指定字符串作为初始化参数 // istringstream 会屏蔽空格 istringstream istr("hello world! I love c++!"); string output; while (istr >> output) { cout << output << " "; } cout << endl; stringstream ss; ss << "hello world"; string str; while (ss >> str) { cout << str << endl; }
6. C++11新特性
1. 初始化方式
使用{}
可以完成数组或者变量的初始化。
2. 声明
-
auto 自动类型推导,没什么可说的了
-
decltype:指定一个变量的类型和一个变量相同
double x; int n; decltype (x*n) q; // q:double decltype (&n) p; // p:int* decltype ((n)) m; // m:int &
这种定义的方式在模板种非常有用,因为在调用时才知道变量的类型,所以需要将变量的类型设置成相同的。
3. 有关类
支持在类内给类成员进行初始化
4. 有关模板和STL
-
增强for循环
for(auto &num : nums) { …… }
-
新增容器
-
unordered_xxx
-
forward_list 单向链表
-
array 在定义时要指定固定的元素个数
std::array<int, 360> ar;
-
-
新的STL方法
- cbegin(),cend() 表示const,只读不可修改
- crbegin(), crend() 只读
5. 右值引用与移动语义
移动语义:用于消除一些额外的中间变量的产生,以提高程序执行的效率。
要让移动语义发生,要有两个步骤:
第一步:要让编译器知道何时使用移动语义。
Useless two = one; // 调用拷贝构造函数
Useless three = (one + two); // 移动语义右值引用,直接转移one+two到所有权到three
第二步:编写移动语义对应的函数,处理其中的逻辑。
-
如果想让一个普通的对象也使用移动语义的方式,那么就需要使用强制移动
#include <utility> Useless four = std::move(one); //此时会调用右值引用的函数
右值语义的意义在于编写能够利用右值引用实现库代码,例如:STL包含移动构造函数。
6. 新的类的功能
-
特殊的成员函数:
// 提供了默认的移动构造函数和移动运算符 Someclass::Someclass(Someclass &&); Someclass& Someclass::operator(Someclass &&);
-
与以前的类的方法定义规则相同,如果只显示定义了移动构造函数,那么默认构造函数、拷贝构造函数都不会被自动定义出来,可以使用
函数声明=default;
的方法来进行默认的函数的构造。class Someclass { public: Someclass(Someclass &&); Someclass() = default; Someclass(const Someclass &) = default; Someclass & operator=(const Someclass &) = default; ... };
-
如果想要禁用某个函数,那么就使用
类成员函数声明=delete
Someclass(const Someclass &) = delete;
注意:关键字default只能用于6个特殊的成员函数,delete可以作用于全部的类的成员函数。
-
委托构造函数
-
继承构造函数
class C1 { public: int fn(int j){...} ... }; class C1 { public: using C1::fn; //如果C1中的fn有重载的版本的话,也一并继承 ... };
-
管理虚方法
- override
- 如果当子类重写虚方法的时候没有完全按照虚函数的定义书写,那么将会出现隐藏了原有的虚函数的问题,一些函数的调用将无法使用。
- 在重写虚函数的函数后面加上override,如果重写的函数与虚函数不匹配的话,将会报错
virtual void f(char* ch) const override { ... }
- final
- 如果想禁止派生类覆盖特定的虚方法,可以在参数列表后面加上final
virtual void f(char ch) const final { ... }
7. lambda表达式
-
lambda表达式的好处:可以直接在函数的调用处写出处理的逻辑,坏处就是无法进行复用。给lambda表达式添加一个定义即可对它进行命名和复用
vector<int> n1 = {1, 2, 3, 4, 5, 6}; vector<int> n2 = {1, 2, 3, 4, 5, 6}; auto mod = [](){return x % 3 == 0;}; count1 = std::count_if(n1.begin(), n1.end(), mod); count2 = std::count_if(n2.begin(), n2.end(), mod);
-
[]中的符号
- = :代表可以访问作用域内的任何动态变量,其名称可以放在小括号内,传递形式是值传递
- & : 作用同上,传递形式是引用传递
8. 可变参模板函数
使用... args
来表示可变参
template<typename T>
void show_list(const T& value)
{
std::cout << value << "\n";
}
template<typename T, typename... Args>
void show_list(const T& value, const Args&... args)
{
std::cout << value << ",";
show_list(args...);
}
注意如上函数的声明,函数二的第一个参数是为了对show_list的递归调用每次消除一个参数,如果不这样消除参数,那么将会一直循环递归无法停下。然后还需要再定义一个只有一个参数的函数用于进行最终值的接收。
函数调用的时候可以传递不同类型的参数。