栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

C++ 新特性
  • 智能指针
    • 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 共享指针

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

基本用法
  1. 初始化方法

构造函数初始化

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

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

  1. 指定删除器

shared_ptr 指向没有析构函数的变量,需要指定删除器

std::shared_ptr p(new int(1), [](int *p) { 
	cout << "call lambda delete p" << endl; 
	delete p;});
std::shared_ptr p3(new int[10], [](int *p) { delete [] p;});
使用 shared_ptr 的问题
  1. 不要使用一个原始指针初始化多个shared_ptr
int *ptr = new int;
shared_ptr p1(ptr);
shared_ptr p2(ptr); // 逻辑错误
  1. 不要在函数实参中创建shared_ptr
function(shared_ptr(new int), g()); //有缺陷
  1. 通过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;
}

  1. 避免循环引用
#include 
#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_ptr 特性
  1. 独占型智能指针
unique_ptr my_ptr(new T); 
unique_ptr my_other_ptr = my_ptr; // 报错,不能复制
  1. 不允许复制
unique_ptr my_ptr(new T); // 正确 
unique_ptr my_other_ptr = std::move(my_ptr); // 正确 
unique_ptr ptr = my_ptr; // 报错,不能复制
  1. C++14中更新的make_unique
auto upw1(std::make_unique()); // with make func 
std::unique_ptr upw2(new Widget); // without make func
unique_ptr 和 shared_ptr 的区别
  1. unique_ptr 可以指向一个数组
std::unique_ptr ptr(new int[10]); 
ptr[9] = 9; 
std::shared_ptr ptr2(new int[10]); // 这个是不合法的
  1. unique_ptr 和 shared_ptr 指定删除器的区别
std::shared_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指针解决循环引用问题

weak_ptr 的基本用法
  1. 通过use_count()方法获取当前观察资源的引用计数
shared_ptr sp(new int(10)); 
weak_ptr wp(sp); 
cout << wp.use_count() << endl; //结果输出1
  1. 通过expired()方法观察资源是否已经释放
shared_ptr sp(new int(10)); 
weak_ptr wp(sp); 
if(wp.expired()) 
	cout << "weak_ptr无效,资源已释放"; 
else
	cout << "weak_ptr有效";
  1. 通过lock方法获取监视的shared_ptr
std::weak_ptr 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; 
}
weak_ptr 返回this指针

同之前使用shared_from_this

weak_ptr 解决循环引用的问题

这里可以解决上面提过的循环引用的问题

#include 
#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 使用注意事项
  1. weak_ptr 使用前要检查合法性
weak_ptr wp; 
{ 
	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);
智能指针使用场景

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

右值引用和移动语义

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

&&的特性

生命周期与引用变量一样

右值引用优化性能,避免深拷贝
  1. 默认构造是浅拷贝,直接拷贝地址,导致重复删除
#include 

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 

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 

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 
#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;
}

forward 完美转发

传递之后,左值依然是左值,右值依然是右值

不可对左值进行右值引用

int &&a = 10; 
int &&b = a; //错误

使用forward对左值进行右值引用

int &&a = 10; 
int &&b = std::forward(a);
#include 

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;
}

emplace_back() 减少内存拷贝和移动

C++中使用了 emplack_back() 原地构造效率更高

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

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

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

我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号