C++ 新特性
智能指针
shared_ptr 共享指针
shared_ptr 使用共享计数器
最后一个共享指针销毁时,被指向的对象销毁
基本用法
- 初始化方法
构造函数初始化
// 智能指针初始化
std::shared_ptr<int> p1(new int(1));
std::shared_ptr<int> p2 = p1;
std::shared_ptr<int> p3;
p3.reset(new int(1));
if(p3) {
cout << "p3 is not null";
}
make_shared初始化
auto sp1 = make_shared<int>(100);
//相当于
shared_ptr<int> sp1(new int(100));
不可以使用直接赋值初始化,使用reset初始化
std::shared_ptr<int> p = new int(1);
- 获取原始指针
std::shared_ptr<int> ptr(new int(1));
int *p = ptr.get();
不要保存get()返回值
指针随时可能变成空指针
不要delete返回的返回值
- 指定删除器
shared_ptr 指向没有析构函数的变量,需要指定删除器
std::shared_ptr<int> p(new int(1), [](int *p) {
cout << "call lambda delete p" << endl;
delete p;});
std::shared_ptr<int> p3(new int[10], [](int *p) { delete [] p;});
使用 shared_ptr 的问题
- 不要使用一个原始指针初始化多个shared_ptr
int *ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr); // 逻辑错误
- 不要在函数实参中创建shared_ptr
function(shared_ptr<int>(new int), g()); //有缺陷
- 通过shared_from_this()返回this指针
#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
shared_ptr<A>GetSelf()
{
return shared_ptr<A>(this); // 不要这么做
}
~A()
{
cout << "Deconstruction A" << endl;
}
};
int main()
{
shared_ptr<A> sp1(new A);
shared_ptr<A> sp2 = sp1->GetSelf();
return 0;
}
造成重复析构错误
#include <iostream>
#include <memory>
using namespace std;
class A: public std::enable_shared_from_this<A>
{
public:
shared_ptr<A>GetSelf()
{
return shared_from_this();
}
~A()
{
cout << "Deconstruction A" << endl;
}
};
int main()
{
// shared_ptr<A> sp1(new A);
// shared_ptr<A> sp2 = sp1->GetSelf(); // ok
shared_ptr<A> sp2;
{
shared_ptr<A> sp1(new A);
sp2 = sp1->GetSelf(); // ok
}
cout << "leave {}" << endl;
return 0;
}
- 避免循环引用
#include <iostream>
#include <memory>
using namespace std;
class A;
class B;
class A {
public:
std::shared_ptr<B> bptr;
~A() {
cout << "A is deleted" << endl;
}
};
class B {
public:
std::shared_ptr<A> aptr;
~B() {
cout << "B is deleted" << endl;
}
};
int main()
{
{
std::shared_ptr<A> ap(new A);
std::shared_ptr<B> bp(new B);
ap->bptr = bp;
bp->aptr = ap;
// 是否可以手动释放呢?
}
cout<< "main leave" << endl; // 循环引用导致ap bp退出了作用域都没有析构
return 0;
}
unique_ptr 独占的智能指针
不允许其他指针访问其内部的指针
unique_ptr 特性
- 独占型智能指针
unique_ptr<T> my_ptr(new T);
unique_ptr<T> my_other_ptr = my_ptr; // 报错,不能复制
- 不允许复制
unique_ptr<T> my_ptr(new T); // 正确
unique_ptr<T> my_other_ptr = std::move(my_ptr); // 正确
unique_ptr<T> ptr = my_ptr; // 报错,不能复制
- C++14中更新的make_unique
auto upw1(std::make_unique<Widget>()); // with make func
std::unique_ptr<Widget> upw2(new Widget); // without make func
unique_ptr 和 shared_ptr 的区别
- unique_ptr 可以指向一个数组
std::unique_ptr<int []> ptr(new int[10]);
ptr[9] = 9;
std::shared_ptr<int []> ptr2(new int[10]); // 这个是不合法的
- unique_ptr 和 shared_ptr 指定删除器的区别
std::shared_ptr<int> ptr3(new int(1), [](int *p){delete p;}); // 正确
std::unique_ptr<int> ptr4(new int(1), [](int *p){delete p;}); // 错误
std::unique_ptr<int, void(*)(int*)> ptr5(new int(1), [](int *p){delete p;}); // 正确
weak_ptr 弱引用的智能指针
应对共享指针循环引用的问题:weak_ptr析构不会减少引用计数,只观察共享指针指向的对象
weak_ptr 可以返回this指针解决循环引用问题
weak_ptr 的基本用法
- 通过use_count()方法获取当前观察资源的引用计数
shared_ptr<int> sp(new int(10));
weak_ptr<int> wp(sp);
cout << wp.use_count() << endl; //结果输出1
- 通过expired()方法观察资源是否已经释放
shared_ptr<int> sp(new int(10));
weak_ptr<int> wp(sp);
if(wp.expired())
cout << "weak_ptr无效,资源已释放";
else
cout << "weak_ptr有效";
- 通过lock方法获取监视的shared_ptr
std::weak_ptr<int> 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<int>(42);
gw = sp;
f();
}
f();
return 0;
}
weak_ptr 返回this指针
同之前使用shared_from_this
weak_ptr 解决循环引用的问题
这里可以解决上面提过的循环引用的问题
#include <iostream>
#include <memory>
using namespace std;
class A;
class B;
class A {
public:
std::weak_ptr<B> bptr; // 修改为weak_ptr
~A() {
cout << "A is deleted" << endl;
}
};
class B {
public:
std::shared_ptr<A> aptr;
~B() {
cout << "B is deleted" << endl;
}
};
int main()
{
{
std::shared_ptr<A> ap(new A);
std::shared_ptr<B> bp(new B);
ap->bptr = bp;
bp->aptr = ap;
}
cout<< "main leave" << endl;
return 0;
}
weak_ptr 使用注意事项
- weak_ptr 使用前要检查合法性
weak_ptr<int> wp;
{
shared_ptr<int> sp(new int(1)); //sp.use_count()==1
wp = sp; //wp不会改变引用计数,所以sp.use_count()==1
shared_ptr<int> sp_ok = wp.lock(); //wp没有重载->操作符。只能这样取所指向的对象
}
shared_ptr<int> sp_null = wp.lock(); //sp_null.use_count()==0;
使用弱指针之前需要检查
weak_ptr<int> wp;
shared_ptr<int> sp_ok;
{
shared_ptr<int> 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<A>&sp) {
...
}
..
std::thread td(fn, sp1);
多线程代码操作不同的 shared_ptr 对象 : 多线程函数传值
多线程操作不同对象安全
std::thread td([sp1]()){....});
void fn(shared_ptr<A>sp) {
...
}
..
std::thread td(fn, sp1);
智能指针使用场景
直播场景中,流媒体将数据帧转发给上千观众
右值引用和移动语义
避免无谓的复制,提高性能
&&的特性
生命周期与引用变量一样
右值引用优化性能,避免深拷贝
- 默认构造是浅拷贝,直接拷贝地址,导致重复删除
#include <iostream>
using 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;
}
- 深拷贝创先新变量导致性能损耗
#include <iostream>
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(){
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 <iostream>
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()方法将左值转换成右值
#include <iostream>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <string.h>
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] = '\0';
}
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;
}
forward 完美转发
传递之后,左值依然是左值,右值依然是右值
不可对左值进行右值引用
int &&a = 10;
int &&b = a; //错误
使用forward对左值进行右值引用
int &&a = 10;
int &&b = std::forward<int>(a);
#include <iostream>
using namespace std;
template <class T>
void Print(T &t)
{
cout << "L" << t << endl;
}
template <class T>
void Print(T &&t)
{
cout << "R" << t << endl;
}
template <class T>
void func(T &&t)
{
cout << "func(T &&t)" << endl;
Print(t);
Print(std::move(t));
Print(std::forward<T>(t));
}
//template <class T>
//void func(T &t)
//{
// cout << "func(T &t)" << endl;
// Print(t);
// Print(std::move(t));
// Print(std::forward<T>(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<int>(y))" << endl;
func(std::forward<int>(y)); //
cout << "-- std::move(y)" << endl;
// func(std::move(y)); // 这种情况又怎么样呢?
// func("std::move(y)");
// func(std::forward<string>("std::move(y)"));
return 0;
}
emplace_back() 减少内存拷贝和移动
C++中使用了 emplack_back() 原地构造效率更高
- time_interval.h
#ifndef TIME_INTERVAL_H
#define TIME_INTERVAL_H
#include <iostream>
#include <memory>
#include <string>
#ifdef GCC
#include <sys/time.h>
#else
#include <ctime>
#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<TimeInterval> time_interval_scope_begin = std::make_shared<TimeInterval>(d)
#endif // TIME_INTERVAL_H
- 自行创建一个类,测试性能
#include <vector>
#include <string>
#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<Foo> 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
}
}
unordered_container 无序容器
C++11中增加了无序容器unordered_map/unordered_multimap和unordered_set/unordered_multiset
无序容器使用Hash快速操作元素,需要提供Hash函数和比较函数
map 和 unordered_map 的差别
map:
优点:
有序性
红黑树 效率高
缺点:
空间占用高
unordered_map:
优点:
实现Hash表 查找特定元素快
缺点:
建立Hash表时间长
匿名函数lambda
匿名函数语法
可以省略返回类型
//[捕获列表](参数列表){函数体}
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 | 变量传拷贝,每一个变量前使用& |
C++11标准库(STL)
容器
序列容器: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
算法
插入删除搜索排序等算法