文章目录

  • 4、 c++11 解决内存泄露问题
    • 4.1 shared_ptr
    • 4.2 weak_ptr
    • 4.3 unique_ptr
    • enable_shared_from_this
    • 4.4 通过智能指针管理第三方库分配的内存
    • 其他细节
      • make_shared
  • 5、c++11 让多线程开发变得简单
    • 5.1 线程
    • 5.4 互斥锁、条件变量、原子变量
    • 5.5 call_once/once_flag
    • 5.6 异步操作
  • 6、c++11 便利工具
    • 6.1 chrono 库
    • 6.2 数值类型和字符串的相互转换
    • 6.3 宽窄字符转换
  • 7、 其他特性
    • 7.1 委托构造
    • 7.2 原始字面量
    • 7.3 final 和 override
    • 7.4 内存对齐
      • 堆内存对齐
      • 对齐的几种方式
      • aligend_strorage
      • std::max_align_t

4、 c++11 解决内存泄露问题

c++11 提供了3中智能指针,shared_ptr, weak_ptr, unique_ptr, 使用的时候引用头文件

4.1 shared_ptr

shared_ptr 需要维护引用计数的信息,

强引用, 用来记录当前有多少个存活的 shared_ptrs 正持有该对象. 共享的对象会在最后一个强引用离开的时候销毁( 也可能释放).

弱引用, 用来记录当前有多少个正在观察该对象的 weak_ptrs. 当最后一个弱引用离开的时候, 共享的内部信息控制块会被销毁和释放 (共享的对象也会被释放, 如果还没有释放的话).

shared_ptr 需要注意的问题

  • 1、不能1个指针初始化多个 shared_ptr
  • 2、不要在实参中创建shared_ptr
  • 3、不要将this 作为shared_ptr 返回,因为多次调用会导致构建多个不相关ptr 对象,enable_shared_from_this
  • 4、避免循环引用,会导致内存泄露

实现要点

  1. referCount 接口 addRefCount, release, dispose (删除指针), distory(delete this, 因为自己是指针),有2个属性,一个shared 引用计数,还是有weak 的引用计数。 如果weak 计数不为1 ,不调用distory
  2. shared_ptr, referCount 是指针, 构造中创建new ,拷贝构造referCount指针是相同
  3. 运算符重载 -> , *
  4. 可以支持删除器
/*** shared_ptr 的实现* 1、模板* 2、构造、析构、基类指针*  shared_prt(T*)*  shared_ptr(T*,deletor)*  shared_ptr(const shared_ptr)* *  operator = *  * 3、引用计数,引用计数的多线程安全* 4、*/
namespace my_ns {/** * 1、引用计数中包括了 ptr, 也可以将delete 存储到 refcount 可以继承与Refcount, dispost 时候由子类处理* 2、使用原子变量来进行存储* 3、分成 distory , dispose, release, add 几个接口* 4、可以继续封装 deletor*/template<class T>
class RefCount {public:RefCount() : _count(1) , _ptr(nullptr) {}explicit RefCount(T *ptr,int n): _count(1),_ptr(ptr) {}explicit RefCount(const RefCount<T> &) = delete;~RefCount() = default;//disposevoid dispose() {delete  _ptr;}//destory()void destory() {delete this;}//release()void release() {_count--;std::cout << _count << std::endl;if(_count == 1) {dispose();// 判断weakRefCount 是否为1,如果为1 就删除destory();}}//add_ref_countvoid addRefCount() {_count++;}bool isEmpty() {return _count == 0;}private: std::atomic_int _count;T *_ptr;
};template<class T>
class shared_ptr {public://构造shared_ptr():m_ptr(nullptr), m_refCount(nullptr) {}shared_ptr(T *ptr): m_ptr(ptr), m_refCount(nullptr) {m_refCount = new RefCount<T>(ptr,0);m_refCount->addRefCount();}shared_ptr(const shared_ptr<T> & other) {m_refCount = other.m_refCount; //在类中可以访问类其他对象的私有变量m_refCount->addRefCount();m_ptr = other.m_ptr;}//判断是否兼容template<class T1, class = typename std::enable_if<std::is_convertible<T1 *, T*>::value,T1>::type>shared_ptr(const shared_ptr<T1> & other) {m_refCount = other.m_refCount; //在类中可以访问类其他对象的私有变量m_refCount->addRefCount();m_ptr = other.m_ptr;}~shared_ptr() {//引用计数如果等于if (m_refCount != nullptr)m_refCount->release();}// 赋值构造,是本身已经存在值void reset() {}T * get() {return m_ptr;}// 运算符重载T* operator->() {return m_ptr;}T& operator *() {return *m_ptr;}private:RefCount<T> *m_refCount;T* m_ptr;
};struct A {A() {std::cout << "A::construtor" << std::endl;}~A() {std::cout << "A::destructor" << std::endl;}
};void test_my_shared_ptr() {shared_ptr<A> sp(new A());auto b = sp;
}}// namespace int main() {my_ns::test_my_shared_ptr();std::cout <<"--------"<<std::endl;system("pause");
}

4.2 weak_ptr

对shared_ptr 观察,为了监视shared_ptr 的声明周期,引用计数不会加1,没有重载 *,-> 因为不共享指针,不能操作资源。

// 使用std::shared_ptr<int> sp(new int(10));std::weak_ptr<int> wp(sp);//判断是否失效if(wp.expired()) {}//lock 方法,获取shared_ptrauto sp1 = wp.lock();
// 判断是否过期,_M_refcount 监视与shared_ptr 中_M_refcount __weak_count {//构造__weak_count(const __shared_count<_Lp>& __r) noexcept: _M_pi(__r._M_pi){if (_M_pi != nullptr)_M_pi->_M_weak_add_ref();}long_M_get_use_count() const noexcept{ return _M_pi != nullptr ? _M_pi->_M_get_use_count() : 0; }_Sp_counted_base<_Lp>*  _M_pi;
}//构造weak_ptr {template<typename _Tp1, typename = _Convertible<_Tp1*>>__weak_ptr(const __shared_ptr<_Tp1, _Lp>& __r) noexcept: _M_ptr(__r._M_ptr), _M_refcount(__r._M_refcount){ }boolexpired() const noexcept{ return _M_refcount._M_get_use_count() == 0; }shared_ptr<_Tp>lock() const noexcept{ return shared_ptr<_Tp>(*this, std::nothrow); }_Tp*       _M_ptr;         // Contained pointer.__weak_count<_Lp>  _M_refcount;    // Reference counter.}shared_ptr :: refercount 中,是否保留指针,还需要看看weak_ptr 中是否存在,如果存在就保留
//release()void release() {_count--;std::cout << _count << std::endl;if(_count == 1) {dispose();// 判断weakRefCount 是否为1,如果为1 就删除destory();}}

4.3 unique_ptr

只希望一个智能指针的管理资源

  • 1、是独占的,不共享内部的指针
  • 2、没有赋值构造
  • 3、有移动构造
  • 4、删除器,需要指定类型,shard_ptr 不需要
  • 5、删除器使用lambda 的时候,不能捕获,因为捕获了就无法转换成函数指针。

enable_shared_from_this

需要在内部使用this的情况,例如回调参数是this, 可以shared_from_this

1、创建对象的使用 _M_weak_this 是无效

2、创建一个新的shared_ptr 的时候,会调用__enable_shared_from_this_helper 设置当前对象的_M_weak_this

3、然后在调用shared_from_this 的使用就会和已经共享的对象共享了

4.4 通过智能指针管理第三方库分配的内存

1、资源泄露问题

2、删除依赖于删除器

其他细节

make_shared

优点:
可以统一申请内存,减少一次内存申请
执行顺序不固定,异常导致的内存泄露问题

缺点:
如果weak 监视的情况无法早点释放内存

可以先看下图在看代码
https://www.processon.com/view/link/6145581f0e3e74524c90f11a

make_shared

template<typename _Tp, _Lock_policy _Lp, typename _Alloc, typename... _Args>inline __shared_ptr<_Tp, _Lp>__allocate_shared(const _Alloc& __a, _Args&&... __args){return __shared_ptr<_Tp, _Lp>(_Sp_make_shared_tag(), __a,std::forward<_Args>(__args)...);}template<typename _Tp, _Lock_policy _Lp, typename... _Args>inline __shared_ptr<_Tp, _Lp>__make_shared(_Args&&... __args){typedef typename std::remove_const<_Tp>::type _Tp_nc;return std::__allocate_shared<_Tp, _Lp>(std::allocator<_Tp_nc>(),std::forward<_Args>(__args)...);}

shared_ptr

 template<typename _Alloc, typename... _Args>__shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a,_Args&&... __args): _M_ptr(), _M_refcount(__tag, (_Tp*)0, __a,std::forward<_Args>(__args)...){// _M_ptr needs to point to the newly constructed object.// This relies on _Sp_counted_ptr_inplace::_M_get_deleter.void* __p = _M_refcount._M_get_deleter(typeid(__tag));_M_ptr = static_cast<_Tp*>(__p);__enable_shared_from_this_helper(_M_refcount, _M_ptr, _M_ptr);}__shared_count<_Lp>  _M_refcount;    // Reference counter.

__shared_count

    //在创建的时候,_Sp_counted_ptr_inplace 的对象中包括了所有对象的内存template<typename _Tp, typename _Alloc, typename... _Args>__shared_count(_Sp_make_shared_tag, _Tp*, const _Alloc& __a,_Args&&... __args): _M_pi(0){typedef _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> _Sp_cp_type;typename _Sp_cp_type::__allocator_type __a2(__a);auto __guard = std::__allocate_guarded(__a2);_Sp_cp_type* __mem = __guard.get();// use_count, weak_coun, _Tp的的内存的大小::new (__mem) _Sp_cp_type(std::move(__a),std::forward<_Args>(__args)...);_M_pi = __mem;__guard = nullptr;}_Sp_counted_base<_Lp>*  _M_pi;

_Sp_counted_ptr_inplace 的实现

_M_ptr()返回的就在上面代码中_M_pi 中的偏移位置,也就先申请了所有的内存后,然后在对应的地址上进行构造

template<typename... _Args>_Sp_counted_ptr_inplace(_Alloc __a, _Args&&... __args): _M_impl(__a){// _GLIBCXX_RESOLVE_LIB_DEFECTS// 2070.  allocate_shared should use allocator_traits<A>::constructallocator_traits<_Alloc>::construct(__a, _M_ptr(),std::forward<_Args>(__args)...); // might throw}

5、c++11 让多线程开发变得简单

5.1 线程

  • std::thread
  • join
  • detach //thread 析构后是没有问题
1、std::thread t(func,1,2,"test"); //可以接收参数2、线程出了作用域会析构,如果线程还没有执行完成会发生错误3、可以移动,但不能复制4、std::thread::hardware_concurrency() 核数5、std::sleep_for(std::chrono::seconds)

5.4 互斥锁、条件变量、原子变量

请参考文档:进程&线程-总结.md
链接:http://note.youdao.com/noteshare?id=6fadb16a3437cdc05d57a55b360a12b9&sub=96D0879453A04C5B901F051B383C429F

5.5 call_once/once_flag

std::once_flag flag;std::call_noce(flag, [](){ std::cout << "" ;});

5.6 异步操作

和线程不同的点,每个任务单独起一个线程进行处理,所以最后使用线程池

c++11 提供了异步操作,主要有future, promise, package_task

future 可以完成异步调用的返回值,异步结果的传输通道,可以方便的获取线程函数的返回值。可以轮询查询结果

/*** future 有3种状态*  1、deferred 异步操作还没开始*  2、reader 异步操作已经完成*  3、timeout 异步操作超时* * 容器存储使用shared_future*/void test_future() {std::future_status status;std::future<int> fu;do {status = fu.wait_for(std::chrono::seconds());} while(status != std::future_status::ready);std::vector<std::shared_future<int>> vec;//vec.push_back(fu);
}

promise ,传递参数方式

/*** promise 是内部存储了future , 可以通过线程传递*/
void test_promise() {std::promise<int> pr;std::thread t([](std::promise<int> & p){p.set_value_at_thread_exit(9);},std::ref(pr));auto fu = pr.get_future();auto r = fu.get();
}

package_task 一种task 的封装

/*** package_task* 1、和promise 有点像,promise 是共享对象,通过参数传递的方式* 2、package_task 保存的是函数调用*/void test_package_task() {std::packaged_task<int()> task([](){return 7;});//函数的返回结果,作为futurestd::thread t(std::ref(task));auto fu = task.get_future();auto r1 = fu.get();
}

async 是不用关注线程的创建细节,异步任务优先使用async

/*** async 可以创建thread ,* 1、返回值在future中,可以通过future get 或者结果。* 2、如果没有返回值,可以future wait 等待完成, 类似join* 3、async 可以替换thread 做异步处理*/
void test_aysnc() {std::future<int> f1 = std::async(std::launch::async, []() {return 8;});std::cout << f1.get() << std::endl;
}

6、c++11 便利工具

6.1 chrono 库

chrono 库主要包括3中类型

  • 时间间隔
  • 时钟
  • 时间点

1、duration

#include <iostream>
#include <chrono>void test_chrono() {std::chrono::seconds(3);std::chrono::duration<double, std::ratio<1,1000>> ms1(3); //毫秒std::chrono::duration<double, std::ratio<1,1>> d1(3); //秒std::chrono::duration<double, std::ratio<60,1>> m1(3); //分钟auto ms2 = std::chrono::duration_cast<std::chrono::microseconds>(m1);ms2.count();
}

2、时间点


#include <iomanip>
#include <ctime>
#include <ratio>
void test_point() {using namespace std::chrono;time_point<system_clock, seconds> today = time_point_cast<seconds>(system_clock::now());std::cout << today.time_since_epoch().count();//支持运算system_clock::time_point now = system_clock::now();std::time_t last = system_clock::to_time_t(now - hours(24));std::cout <<  std::put_time(std::localtime(&last), "%F %T");
}

3、clock

void test_clock() {using namespace std::chrono;system_clock::time_point t1 = system_clock::now();auto t2 = system_clock::now();std::cout << (t2-t1).count() << std::endl;//将time_point 转换成ctimestd::time_t now_c = system_clock::to_time_t(t1);auto t3 = system_clock::from_time_t(now_c);//high_resolution_clock 高精度auto t4 = high_resolution_clock::now();auto elapsed = duration_cast<microseconds>(high_resolution_clock::now() - t4).count();
}

6.2 数值类型和字符串的相互转换

c++11 提供了to_string 方法

字符串转成数字 atoi, atof, atoll

#include <string>
void test_to_string() {auto str = std::to_string(1);int i = std::atoi(str.c_str());
}

6.3 宽窄字符转换

c++11 增加了unicode字面量的支持,可以通过L定义宽字符

utf-8是在字符,utf-16是宽字符,但是宽字符是每个国家的解释不同

#include <codecvt>
void test_wstring() {std::wstring str = L"中国人";std::wstring_convert<std::codecvt<wchar_t,char,std::mbstate_t>> conv(new std::codecvt<wchar_t, char, std::mbstate_t>("CHS"));std::string narrowStr = conv.to_bytes(str);std::wstring wstr = conv.from_bytes(narrowStr);std::cout << narrowStr << std::endl;std::wcout.imbue(std::locale("chs"));std::wcout << wstr << std::endl;}

7、 其他特性

7.1 委托构造

1、委托构造中不能 成员初始化
2、减少重复代码

7.2 原始字面量

R"( )"

7.3 final 和 override

final 只能修饰virtual 方法
final 可以修饰类

struct A{}B 不可以被继承
struct B final : A {}

7.4 内存对齐

如果内存对齐,数据类型定义的变量的地址都是8个倍数

从C++17开始,可以使用aligned_alloc函数达到这个目的,但是如果使用较老的C++版本,如C++14,C++11,我们需要手动写一个实现。

void* aligned_malloc(size_t size, size_t alignment)
{size_t offset = alignment - 1 + sizeof(void*); // 对齐大小 + 保存原始指针void * originalP = malloc(size + offset); //size_t originalLocation = reinterpret_cast<size_t>(originalP); // 原始指针//对齐的指针,在前面预留最少void* 的大小,所以需要加上offsetsize_t realLocation = (originalLocation + offset) & ~(alignment - 1); void * realP = reinterpret_cast<void*>(realLocation);size_t originalPStorage = realLocation - sizeof(void*); //向前移动void* 大小,保存原始地址*reinterpret_cast<void**>(originalPStorage) = originalP; //保存原始地址return realP;
}void aligned_free(void* p)
{//前移void* 找到真实的地址然后释放size_t originalPStorage = reinterpret_cast<size_t>(p) - sizeof(void*);free(*reinterpret_cast<void**>(originalPStorage));
}

c++11 可以使用aligned_storage 申请一块内存,然后在使用placement new

 static std::aligned_storage<sizeof(A),alignof(A)>::type data;A *attr = new (&data) A;

堆内存对齐

实现申请的内存对齐的方法

  • msvc _aligned_malloc ,由crt 提供
  • linux memalign

对齐的几种方式

#pragma pack(8)
__declspec(align(16)) struct Mystruct {};
struct alignas(16) Mystruct1 {};
struct Mystruct2 {} __attribute__ ((aligned(8)));

aligend_strorage

可以看成一个内存的缓冲区,先由aligend_storage 申请内存块,如果是申请堆申请,还需要使用_aligned_malloc, 会给出警告, 原因是new 不能超出默认最大对齐的是正确的。

struct alignas(32) Mystruct1 {};
void *p = new MyStruct; /// warning, object allocated on the heap may not be aligned 32

std::max_align_t

返回平台的最大默认内存对齐类型,alignof(std::max_align_t) 获取大小

c++11 总结-2相关推荐

  1. Redis 笔记(11)— 文本协议 RESP(单行、多行字符串、整数、错误、数组、空值、空串格式、telnet 登录 redis)

    RESP 是 Redis 序列化协议Redis Serialization Protocol 的简写.它是一种直观的文本协议,优势在于实现异常简单,解析性能极好. ​ Redis 协议将传输的结构数据 ...

  2. Linux shell 学习笔记(11)— 理解输入和输出(标准输入、输出、错误以及临时重定向和永久重定向)

    1. 理解输入和输出 1.1 标准文件描述符 Linux 系统将每个对象当作文件处理.这包括输入和输出进程.Linux 用文件描述符(file descriptor)来标识每个文件对象.文件描述符是一 ...

  3. 只要5分钟用数据可视化带你看遍11月份新闻热点事件

    2017年11月份已经离我们而去,在过去的11月份我们也许经历了双十一的剁手,也可能亲眼看见了别人剁手.11月份的北京大兴区发生了"11·18"重大火灾,国内多家幼儿园也多次上了头 ...

  4. 1-1 机器学习和深度学习综述-paddle

    课程>我的课程>百度架构师手把手教深度学习>1-1 机器学习和深度学习综述> 1-1 机器学习和深度学习综述 paddle初级课程 王然(学生) Notebook 教育 初级深 ...

  5. CUDA 11功能清单

    CUDA 11功能清单 基于NVIDIA Ampere GPU架构的新型NVIDIA A100 GPU在加速计算方面实现了最大的飞跃.A100 GPU具有革命性的硬件功能,CUDA 11与A100一起 ...

  6. 保护嵌入式802.11 Wi-Fi设备时需要考虑的10件事

    保护嵌入式802.11 Wi-Fi设备时需要考虑的10件事 10 things to consider when securing an embedded 802.11 Wi-Fi device 随着 ...

  7. CUDA 11功能展示

    CUDA 11功能展示 CUDA 11 Features Revealed 新的NVIDIA A100 GPU基于NVIDIA安培GPU架构,实现了加速计算的最大一代飞跃.A100 GPU具有革命性的 ...

  8. 深度学习11个实用技巧

    深度学习11个实用技巧 深度学习工程师George Seif发表了一篇博文,总结了7个深度学习的技巧,本文增加了几个技巧,总结了11个深度学习的技巧,主要从提高深度学习模型的准确性和速度两个角度来分析 ...

  9. 【CV】吴恩达机器学习课程笔记第11章

    本系列文章如果没有特殊说明,正文内容均解释的是文字上方的图片 机器学习 | Coursera 吴恩达机器学习系列课程_bilibili 目录 11 机器学习系统设计 11-1 确定执行的优先级:以垃圾 ...

  10. 零起点学算法11——求梯形面积

    零起点学算法11--求梯形面积 Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lld Description 水题 Input ...

最新文章

  1. 区块链基础--工作量证明
  2. teamviewer 可用设备上限_2020推荐香河气压罐专业供水设备
  3. 猫晚流量再创记录,阿里云直播方案护航优酷2500万用户体验
  4. C++实现软件自动更新功能
  5. 蓝桥杯 每周一练 第一周(3n+1问题)
  6. druid不能close mysql连接_druid长时间无操作无法保持连接!!
  7. Vue3 Composition API(一)——setup、reactive、ref、readonly
  8. 计算机软件服务板块,信息技术板块
  9. oracle8i+下载,oracle database 8i, 9i,10g, 11g正确下载地址
  10. linux 读取 ntfs硬盘,嵌入式linux下ntfs格式的硬盘读写方法
  11. 苹果屏幕镜像_给你们科普一下手机投屏和镜像的区别
  12. 手机卡顿怎么办?学会这三个方法清理内存,手机多用三年都不卡
  13. 微软苏州校招笔试 12月27日
  14. 简历职称 计算机,个人简历专业技术职务怎么填 就是你所学的专业技术是你取得...
  15. Windows 提权
  16. bzoj4391 [Usaco2015 dec]High Card Low Card题解
  17. eog命令在播放图片时候的用法总结
  18. 风能设备物流的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  19. RHCSA 核心考点列表
  20. 手机微信广告页html代码,微信公众号h5网页被嵌入广告 不知道什么原因

热门文章

  1. python中可变参数args传入函数时储存的类型是,Python函数可变参数定义及其参数传递方式实例详解...
  2. Vmware虚拟机的单用户模式
  3. Android日常开发 - FlexboxLayout学习笔记
  4. Vue中关于scoped以及scoped样式穿透的原理与使用详解
  5. 【EI会议推荐】第六届先进电子材料、计算机与软件工程国际学术会议(AEMCSE 2023)
  6. 在计算机中程序主要存放在什么中,在计算机中.指令主要存放在( )中.A.存储器 B.键盘 C.鼠标 D.运算器——青夏教育精英家教网——...
  7. linux tmp 目录 权限不够,Linux /tmp目录下执行脚本失败提示Permission denied
  8. C. 实验3_B_小学奥数题
  9. 使用PHP免费发送定时短信
  10. Frida java/js 类型转换