c++11 总结-2
文章目录
- 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、避免循环引用,会导致内存泄露
实现要点
- referCount 接口 addRefCount, release, dispose (删除指针), distory(delete this, 因为自己是指针),有2个属性,一个shared 引用计数,还是有weak 的引用计数。 如果weak 计数不为1 ,不调用distory
- shared_ptr, referCount 是指针, 构造中创建new ,拷贝构造referCount指针是相同
- 运算符重载 -> , *
- 可以支持删除器
/*** 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相关推荐
- Redis 笔记(11)— 文本协议 RESP(单行、多行字符串、整数、错误、数组、空值、空串格式、telnet 登录 redis)
RESP 是 Redis 序列化协议Redis Serialization Protocol 的简写.它是一种直观的文本协议,优势在于实现异常简单,解析性能极好. Redis 协议将传输的结构数据 ...
- Linux shell 学习笔记(11)— 理解输入和输出(标准输入、输出、错误以及临时重定向和永久重定向)
1. 理解输入和输出 1.1 标准文件描述符 Linux 系统将每个对象当作文件处理.这包括输入和输出进程.Linux 用文件描述符(file descriptor)来标识每个文件对象.文件描述符是一 ...
- 只要5分钟用数据可视化带你看遍11月份新闻热点事件
2017年11月份已经离我们而去,在过去的11月份我们也许经历了双十一的剁手,也可能亲眼看见了别人剁手.11月份的北京大兴区发生了"11·18"重大火灾,国内多家幼儿园也多次上了头 ...
- 1-1 机器学习和深度学习综述-paddle
课程>我的课程>百度架构师手把手教深度学习>1-1 机器学习和深度学习综述> 1-1 机器学习和深度学习综述 paddle初级课程 王然(学生) Notebook 教育 初级深 ...
- CUDA 11功能清单
CUDA 11功能清单 基于NVIDIA Ampere GPU架构的新型NVIDIA A100 GPU在加速计算方面实现了最大的飞跃.A100 GPU具有革命性的硬件功能,CUDA 11与A100一起 ...
- 保护嵌入式802.11 Wi-Fi设备时需要考虑的10件事
保护嵌入式802.11 Wi-Fi设备时需要考虑的10件事 10 things to consider when securing an embedded 802.11 Wi-Fi device 随着 ...
- CUDA 11功能展示
CUDA 11功能展示 CUDA 11 Features Revealed 新的NVIDIA A100 GPU基于NVIDIA安培GPU架构,实现了加速计算的最大一代飞跃.A100 GPU具有革命性的 ...
- 深度学习11个实用技巧
深度学习11个实用技巧 深度学习工程师George Seif发表了一篇博文,总结了7个深度学习的技巧,本文增加了几个技巧,总结了11个深度学习的技巧,主要从提高深度学习模型的准确性和速度两个角度来分析 ...
- 【CV】吴恩达机器学习课程笔记第11章
本系列文章如果没有特殊说明,正文内容均解释的是文字上方的图片 机器学习 | Coursera 吴恩达机器学习系列课程_bilibili 目录 11 机器学习系统设计 11-1 确定执行的优先级:以垃圾 ...
- 零起点学算法11——求梯形面积
零起点学算法11--求梯形面积 Time Limit: 1 Sec Memory Limit: 64 MB 64bit IO Format: %lld Description 水题 Input ...
最新文章
- 区块链基础--工作量证明
- teamviewer 可用设备上限_2020推荐香河气压罐专业供水设备
- 猫晚流量再创记录,阿里云直播方案护航优酷2500万用户体验
- C++实现软件自动更新功能
- 蓝桥杯 每周一练 第一周(3n+1问题)
- druid不能close mysql连接_druid长时间无操作无法保持连接!!
- Vue3 Composition API(一)——setup、reactive、ref、readonly
- 计算机软件服务板块,信息技术板块
- oracle8i+下载,oracle database 8i, 9i,10g, 11g正确下载地址
- linux 读取 ntfs硬盘,嵌入式linux下ntfs格式的硬盘读写方法
- 苹果屏幕镜像_给你们科普一下手机投屏和镜像的区别
- 手机卡顿怎么办?学会这三个方法清理内存,手机多用三年都不卡
- 微软苏州校招笔试 12月27日
- 简历职称 计算机,个人简历专业技术职务怎么填 就是你所学的专业技术是你取得...
- Windows 提权
- bzoj4391 [Usaco2015 dec]High Card Low Card题解
- eog命令在播放图片时候的用法总结
- 风能设备物流的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
- RHCSA 核心考点列表
- 手机微信广告页html代码,微信公众号h5网页被嵌入广告 不知道什么原因
热门文章
- python中可变参数args传入函数时储存的类型是,Python函数可变参数定义及其参数传递方式实例详解...
- Vmware虚拟机的单用户模式
- Android日常开发 - FlexboxLayout学习笔记
- Vue中关于scoped以及scoped样式穿透的原理与使用详解
- 【EI会议推荐】第六届先进电子材料、计算机与软件工程国际学术会议(AEMCSE 2023)
- 在计算机中程序主要存放在什么中,在计算机中.指令主要存放在( )中.A.存储器 B.键盘 C.鼠标 D.运算器——青夏教育精英家教网——...
- linux tmp 目录 权限不够,Linux /tmp目录下执行脚本失败提示Permission denied
- C. 实验3_B_小学奥数题
- 使用PHP免费发送定时短信
- Frida java/js 类型转换