C++ 新特性 智能指针 右值引用 移动语义 匿名函数lambda C++标准库STL

智能指针

shared_ptr 共享指针

shared_ptr 使用共享计数器
最后一个共享指针销毁时,被指向的对象销毁

基本用法

  1. 初始化方法

构造函数初始化

// 智能指针初始化 
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);
  1. 获取原始指针
std::shared_ptr<int> ptr(new int(1)); 
int *p = ptr.get();

不要保存get()返回值
指针随时可能变成空指针
不要delete返回的返回值

  1. 指定删除器

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 的问题

  1. 不要使用一个原始指针初始化多个shared_ptr
int *ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr); // 逻辑错误
  1. 不要在函数实参中创建shared_ptr
function(shared_ptr<int>(new int), g()); //有缺陷
  1. 通过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;
}

  1. 避免循环引用
#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 特性

  1. 独占型智能指针
unique_ptr<T> my_ptr(new T); 
unique_ptr<T> my_other_ptr = my_ptr; // 报错,不能复制
  1. 不允许复制
unique_ptr<T> my_ptr(new T); // 正确 
unique_ptr<T> my_other_ptr = std::move(my_ptr); // 正确 
unique_ptr<T> ptr = my_ptr; // 报错,不能复制
  1. 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 的区别

  1. unique_ptr 可以指向一个数组
std::unique_ptr<int []> ptr(new int[10]); 
ptr[9] = 9; 
std::shared_ptr<int []> ptr2(new int[10]); // 这个是不合法的
  1. 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 的基本用法

  1. 通过use_count()方法获取当前观察资源的引用计数
shared_ptr<int> sp(new int(10)); 
weak_ptr<int> wp(sp); 
cout << wp.use_count() << endl; //结果输出1
  1. 通过expired()方法观察资源是否已经释放
shared_ptr<int> sp(new int(10)); 
weak_ptr<int> wp(sp); 
if(wp.expired()) 
	cout << "weak_ptr无效,资源已释放"; 
else
	cout << "weak_ptr有效";
  1. 通过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 使用注意事项

  1. 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);

智能指针使用场景

直播场景中,流媒体将数据帧转发给上千观众

右值引用和移动语义

避免无谓的复制,提高性能

&&的特性

生命周期与引用变量一样

右值引用优化性能,避免深拷贝

  1. 默认构造是浅拷贝,直接拷贝地址,导致重复删除
#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;
}

  1. 深拷贝创先新变量导致性能损耗
#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;
}
  1. 移动构造
#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() 原地构造效率更高

  1. 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

  1. 自行创建一个类,测试性能
#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

算法

插入删除搜索排序等算法

STL 使用范例

C++ 参考手册 https://zh.cppreference.com/w/cpp

正则表达式

正则表达式参考:https://zh.cppreference.com/w/cpp/regex

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值