- 智能指针
- shared_ptr 共享指针
- 基本用法
- 使用 shared_ptr 的问题
- unique_ptr 独占的智能指针
- unique_ptr 特性
- unique_ptr 和 shared_ptr 的区别
- weak_ptr 弱引用的智能指针
- weak_ptr 的基本用法
- weak_ptr 返回this指针
- weak_ptr 解决循环引用的问题
- weak_ptr 使用注意事项
- 智能指针安全性问题
- 智能指针使用场景
- 右值引用和移动语义
- &&的特性
- 右值引用优化性能,避免深拷贝
- 移动语义
- forward 完美转发
- emplace_back() 减少内存拷贝和移动
- unordered_container 无序容器
- map 和 unordered_map 的差别
- 匿名函数lambda
- 匿名函数语法
- 捕获列表
- 匿名函数简写
- lambda捕获列表
- C++11标准库(STL)
- 容器
- 迭代器
- 算法
- STL 使用范例
- 正则表达式
基本用法shared_ptr 使用共享计数器
最后一个共享指针销毁时,被指向的对象销毁
- 初始化方法
构造函数初始化
// 智能指针初始化 std::shared_ptrp1(new int(1)); std::shared_ptr p2 = p1; std::shared_ptr p3; p3.reset(new int(1)); if(p3) { cout << "p3 is not null"; }
make_shared初始化
auto sp1 = make_shared(100); //相当于 shared_ptr sp1(new int(100));
不可以使用直接赋值初始化,使用reset初始化
std::shared_ptrp = new int(1);
- 获取原始指针
std::shared_ptrptr(new int(1)); int *p = ptr.get();
不要保存get()返回值
指针随时可能变成空指针
不要delete返回的返回值
- 指定删除器
shared_ptr 指向没有析构函数的变量,需要指定删除器
std::shared_ptrp(new int(1), [](int *p) { cout << "call lambda delete p" << endl; delete p;});
std::shared_ptr使用 shared_ptr 的问题p3(new int[10], [](int *p) { delete [] p;});
- 不要使用一个原始指针初始化多个shared_ptr
int *ptr = new int; shared_ptrp1(ptr); shared_ptr p2(ptr); // 逻辑错误
- 不要在函数实参中创建shared_ptr
function(shared_ptr(new int), g()); //有缺陷
- 通过shared_from_this()返回this指针
#include#include using namespace std; class A { public: shared_ptrGetSelf() { return shared_ptr(this); // 不要这么做 } ~A() { cout << "Deconstruction A" << endl; } }; int main() { shared_ptr sp1(new A); shared_ptr sp2 = sp1->GetSelf(); return 0; }
造成重复析构错误
#include#include using namespace std; class A: public std::enable_shared_from_this { public: shared_ptrGetSelf() { return shared_from_this(); } ~A() { cout << "Deconstruction A" << endl; } }; int main() { // shared_ptr sp1(new A); // shared_ptr sp2 = sp1->GetSelf(); // ok shared_ptr sp2; { shared_ptr sp1(new A); sp2 = sp1->GetSelf(); // ok } cout << "leave {}" << endl; return 0; }
- 避免循环引用
#includeunique_ptr 独占的智能指针#include using namespace std; class A; class B; class A { public: std::shared_ptr bptr; ~A() { cout << "A is deleted" << endl; } }; class B { public: std::shared_ptr aptr; ~B() { cout << "B is deleted" << endl; } }; int main() { { std::shared_ptr ap(new A); std::shared_ptr bp(new B); ap->bptr = bp; bp->aptr = ap; // 是否可以手动释放呢? } cout<< "main leave" << endl; // 循环引用导致ap bp退出了作用域都没有析构 return 0; }
unique_ptr 特性不允许其他指针访问其内部的指针
- 独占型智能指针
unique_ptrmy_ptr(new T); unique_ptr my_other_ptr = my_ptr; // 报错,不能复制
- 不允许复制
unique_ptrmy_ptr(new T); // 正确 unique_ptr my_other_ptr = std::move(my_ptr); // 正确 unique_ptr ptr = my_ptr; // 报错,不能复制
- C++14中更新的make_unique
auto upw1(std::make_uniqueunique_ptr 和 shared_ptr 的区别()); // with make func std::unique_ptr upw2(new Widget); // without make func
- unique_ptr 可以指向一个数组
std::unique_ptrptr(new int[10]); ptr[9] = 9; std::shared_ptr ptr2(new int[10]); // 这个是不合法的
- unique_ptr 和 shared_ptr 指定删除器的区别
std::shared_ptrweak_ptr 弱引用的智能指针ptr3(new int(1), [](int *p){delete p;}); // 正确 std::unique_ptr ptr4(new int(1), [](int *p){delete p;}); // 错误 std::unique_ptr ptr5(new int(1), [](int *p){delete p;}); // 正确
weak_ptr 的基本用法应对共享指针循环引用的问题:weak_ptr析构不会减少引用计数,只观察共享指针指向的对象
weak_ptr 可以返回this指针解决循环引用问题
- 通过use_count()方法获取当前观察资源的引用计数
shared_ptrsp(new int(10)); weak_ptr wp(sp); cout << wp.use_count() << endl; //结果输出1
- 通过expired()方法观察资源是否已经释放
shared_ptrsp(new int(10)); weak_ptr wp(sp); if(wp.expired()) cout << "weak_ptr无效,资源已释放"; else cout << "weak_ptr有效";
- 通过lock方法获取监视的shared_ptr
std::weak_ptrweak_ptr 返回this指针gw; void f() { if(gw.expired()) { cout << "gw无效,资源已释放"; } else { auto spt = gw.lock(); cout << "gw有效, *spt = " << *spt << endl; } } int main() { { auto sp = atd::make_shared (42); gw = sp; f(); } f(); return 0; }
同之前使用shared_from_this
weak_ptr 解决循环引用的问题这里可以解决上面提过的循环引用的问题
#includeweak_ptr 使用注意事项#include using namespace std; class A; class B; class A { public: std::weak_ptr bptr; // 修改为weak_ptr ~A() { cout << "A is deleted" << endl; } }; class B { public: std::shared_ptr aptr; ~B() { cout << "B is deleted" << endl; } }; int main() { { std::shared_ptr ap(new A); std::shared_ptr bp(new B); ap->bptr = bp; bp->aptr = ap; } cout<< "main leave" << endl; return 0; }
- weak_ptr 使用前要检查合法性
weak_ptrwp; { shared_ptr sp(new int(1)); //sp.use_count()==1 wp = sp; //wp不会改变引用计数,所以sp.use_count()==1 shared_ptr sp_ok = wp.lock(); //wp没有重载->操作符。只能这样取所指向的对象 } shared_ptr sp_null = wp.lock(); //sp_null.use_count()==0;
使用弱指针之前需要检查
weak_ptr智能指针安全性问题wp; shared_ptr sp_ok; { shared_ptr sp(new int(1)); //sp.use_count()==1 wp = sp; //wp不会改变引用计数,所以sp.use_count()==1 sp_ok = wp.lock(); //wp没有重载->操作符。只能这样取所指向的对象 } if(wp.expired()) { cout << "shared_ptr is destroy" << endl; } else { cout << "shared_ptr no destroy" << endl; }
多线程代码操作同一个 shared_ptr 对象 : 多线程函数传引用
多线程操作同一个对象不安全
std::thread td([&sp1]()){....});
void fn(shared_ptr&sp) {
...
}
..
std::thread td(fn, sp1);
多线程代码操作不同的 shared_ptr 对象 : 多线程函数传值
多线程操作不同对象安全
std::thread td([sp1]()){....});
void fn(shared_ptrsp) {
...
}
..
std::thread td(fn, sp1);
智能指针使用场景
右值引用和移动语义直播场景中,流媒体将数据帧转发给上千观众
&&的特性避免无谓的复制,提高性能
右值引用优化性能,避免深拷贝生命周期与引用变量一样
- 默认构造是浅拷贝,直接拷贝地址,导致重复删除
#includeusing namespace std; class A { public: A() :m_ptr(new int(0)) { cout << "constructor A" << endl; } ~A(){ cout << "destructor A, m_ptr:" << m_ptr << endl; delete m_ptr; m_ptr = nullptr; } private: int* m_ptr; }; // 为了避免返回值优化,此函数故意这样写 A Get(bool flag) { A a; A b; cout << "ready return" << endl; if (flag) return a; else return b; } int main() { { A a = Get(false); // 运行报错 } cout << "main finish" << endl; return 0; }
- 深拷贝创先新变量导致性能损耗
#includeusing namespace std; class A { public: A() :m_ptr(new int(0)) { cout << "constructor A" << endl; } A(const A& a) :m_ptr(new int(*a.m_ptr)) { cout << "copy constructor A" << endl; } ~A(){ cout << "destructor A, m_ptr:" << m_ptr << endl; delete m_ptr; m_ptr = nullptr; } private: int* m_ptr; }; // 为了避免返回值优化,此函数故意这样写 A Get(bool flag) { A a; A b; cout << "ready return" << endl; if (flag) return a; else return b; } int main() { { A a = Get(false); // 正确运行 } cout << "main finish" << endl; return 0; }
- 移动构造
#include移动语义using namespace std; class A { public: A() :m_ptr(new int(0)) { cout << "constructor A" << endl; } A(const A& a) :m_ptr(new int(*a.m_ptr)) { cout << "copy constructor A" << endl; } A(A&& a) :m_ptr(a.m_ptr) { a.m_ptr = nullptr; cout << "move constructor A" << endl; } ~A(){ cout << "destructor A, m_ptr:" << m_ptr << endl; if(m_ptr) delete m_ptr; } private: int* m_ptr; }; // 为了避免返回值优化,此函数故意这样写 A Get(bool flag) { A a; A b; cout << "ready return" << endl; if (flag) return a; else return b; } int main() { { A a = Get(false); // 正确运行 } cout << "main finish" << endl; return 0; }
move()方法将左值转换成右值
#includeforward 完美转发#include #include #include #include using namespace std; class MyString { private: char* m_data; size_t m_len; void copy_data(const char *s) { m_data = new char[m_len+1]; memcpy(m_data, s, m_len); m_data[m_len] = ' '; } public: MyString() { m_data = NULL; m_len = 0; } MyString(const char* p) { m_len = strlen (p); copy_data(p); } MyString(const MyString& str) { m_len = str.m_len; copy_data(str.m_data); std::cout << "Copy Constructor is called! source: " << str.m_data << std::endl; } MyString& operator=(const MyString& str) { if (this != &str) { m_len = str.m_len; copy_data(str.m_data); } std::cout << "Copy Assignment is called! source: " << str.m_data << std::endl; return *this; } // 用c++11的右值引用来定义这两个函数 MyString(MyString&& str) { std::cout << "Move Constructor is called! source: " << str.m_data << std::endl; m_len = str.m_len; m_data = str.m_data; //避免了不必要的拷贝 str.m_len = 0; str.m_data = NULL; } MyString& operator=(MyString&& str) { std::cout << "Move Assignment is called! source: " << str.m_data << std::endl; if (this != &str) { m_len = str.m_len; m_data = str.m_data; //避免了不必要的拷贝 str.m_len = 0; str.m_data = NULL; } return *this; } virtual ~MyString() { if (m_data) free(m_data); } }; int main() { MyString a; a = MyString("Hello"); // move MyString b = a; // copy MyString c = std::move(a); // move, 将左值转为右值 return 0; }
传递之后,左值依然是左值,右值依然是右值
不可对左值进行右值引用
int &&a = 10; int &&b = a; //错误
使用forward对左值进行右值引用
int &&a = 10; int &&b = std::forward(a);
#includeemplace_back() 减少内存拷贝和移动using namespace std; template void Print(T &t) { cout << "L" << t << endl; } template void Print(T &&t) { cout << "R" << t << endl; } template void func(T &&t) { cout << "func(T &&t)" << endl; Print(t); Print(std::move(t)); Print(std::forward (t)); } //template //void func(T &t) //{ // cout << "func(T &t)" << endl; // Print(t); // Print(std::move(t)); // Print(std::forward (t)); //} int main() { cout << "-- func(1)" << endl; func(1); int x = 10; int y = 20; cout << "-- func(x)" << endl; func(x); // x本身是左值 cout << "-- func(std::forward (y))" << endl; func(std::forward (y)); // cout << "-- std::move(y)" << endl; // func(std::move(y)); // 这种情况又怎么样呢? // func("std::move(y)"); // func(std::forward ("std::move(y)")); return 0; }
C++中使用了 emplack_back() 原地构造效率更高
- time_interval.h
#ifndef TIME_INTERVAL_H #define TIME_INTERVAL_H #include#include #include #ifdef GCC #include #else #include #endif // GCC class TimeInterval { public: TimeInterval(const std::string& d) : detail(d) { init(); } TimeInterval() { init(); } ~TimeInterval() { #ifdef GCC gettimeofday(&end, NULL); std::cout << detail << 1000 * (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec) / 1000 << " ms" << endl; #else end = clock(); std::cout << detail << (double)(end - start) << " ms" << std::endl; #endif // GCC } protected: void init() { #ifdef GCC gettimeofday(&start, NULL); #else start = clock(); #endif // GCC } private: std::string detail; #ifdef GCC timeval start, end; #else clock_t start, end; #endif // GCC }; #define TIME_INTERVAL_SCOPE(d) std::shared_ptr time_interval_scope_begin = std::make_shared (d) #endif // TIME_INTERVAL_H
- 自行创建一个类,测试性能
#includeunordered_container 无序容器#include #include "time_interval.h" using namespace std; class Foo { public: Foo(std::string str) : name(str) { std::cout << "constructor" << std::endl; } Foo(const Foo& f) : name(f.name) { std::cout << "copy constructor" << std::endl; } Foo(Foo&& f) : name(std::move(f.name)){ std::cout << "move constructor" << std::endl; } private: std::string name; }; int main() { std::vector v; int count = 10000000; v.reserve(count); //预分配十万大小,排除掉分配内存的时间 { TIME_INTERVAL_SCOPE("push_back T:"); Foo temp("test"); v.push_back(temp);// push_back(const T&),参数是左值引用 //打印结果: //constructor //copy constructor } cout << " ---------------------n" << endl; v.clear(); { TIME_INTERVAL_SCOPE("push_back move(T):"); Foo temp("test"); v.push_back(std::move(temp));// push_back(T &&), 参数是右值引用 //打印结果: //constructor //move constructor } cout << " ---------------------n" << endl; v.clear(); { TIME_INTERVAL_SCOPE("push_back(T&&):"); v.push_back(Foo("test"));// push_back(T &&), 参数是右值引用 //打印结果: //constructor //move constructor } cout << " ---------------------n" << endl; v.clear(); { std::string temp = "test"; TIME_INTERVAL_SCOPE("push_back(string):"); v.push_back(temp);// push_back(T &&), 参数是右值引用 //打印结果: //constructor //move constructor } cout << " ---------------------n" << endl; v.clear(); { std::string temp = "test"; TIME_INTERVAL_SCOPE("emplace_back(string):"); v.emplace_back(temp);// 只有一次构造函数,不调用拷贝构造函数,速度最快 //打印结果: //constructor } }
map 和 unordered_map 的差别C++11中增加了无序容器unordered_map/unordered_multimap和unordered_set/unordered_multiset
无序容器使用Hash快速操作元素,需要提供Hash函数和比较函数
map:
优点:
有序性
红黑树 效率高
缺点:
空间占用高
unordered_map:
匿名函数lambda 匿名函数语法优点:
实现Hash表 查找特定元素快
缺点:
建立Hash表时间长
可以省略返回类型
//[捕获列表](参数列表){函数体}
int main() {
auto Add = [](int a, int b) {
return a + b;
};
std::cout << Add(1, 2) << std::endl; //输出3
return 0;
}
捕获列表
int main() {
int c = 10;
auto Add = [c](int a, int b) {
return a + b + c;
};
std::cout << Add(1, 2) << std::endl; //输出3
return 0;
}
匿名函数简写
lambda捕获列表可以忽略返回值类型
| names | 传递指定变量名 |
|---|---|
| & | 隐式捕获列表,变量传引用 |
| &,identifier_list | 变量传引用,每一个变量之前不能使用& |
| = | 隐式捕获列表,变量传拷贝 |
| =,identifier_list | 变量传拷贝,每一个变量前使用& |
迭代器序列容器:array/deque/forward_list/list/vector
有序关联容器:set/multiset/map/multimap
无序关联容器:unordered_set/unordered_multiset/unordered_map/unordered_multimap/
容器适配器:stack/queue/priority_queue
算法随机访问迭代器:vector/array/deque
前向迭代器:forward_list
不支持迭代器:stack/queue/priority_queue
双向迭代器:list/set/map
STL 使用范例插入删除搜索排序等算法
正则表达式C++ 参考手册 https://zh.cppreference.com/w/cpp
正则表达式参考:https://zh.cppreference.com/w/cpp/regex



