当前位置: 首页 > article >正文

unique_ptr自定义删除器,_Compressed_pair利用偏特化减少存储的一些设计思路

主要是利用偏特化,

如果自定义删除器是空类(没有成员变量,可以有成员函数):

_Compressed_pair会继承删除器(删除器作为基类),但_Compressed_pair里不保存删除器对象,只保存指针,所以此时,可以把unique_ptr认为是裸指针;

如果不是空类:那么会同时保存删除器对象和指针。

避免错误的使用自定义unique_ptr deleter带来不必要的开销

避免错误的使用自定义unique_ptr deleter带来不必要的开销

LeeCarry

这世界太繁杂了,我只想守护自己的纯粹

25 人赞同了该文章

无意间看到

FOCUS:现代 C++:一文读懂智能指针595 赞同 · 12 评论文章

昂,一个unique_ptr要用40字节???第一反应是作者是不是笔误多打了个0?然而细看一下不对啊,这里应该是用64位测的,一个raw pointer是8字节,那应该不是笔误,再认真细看一下就明白了。

还是从头梳理一下吧,首先要知道unique_ptr和shared_ptr的自定义deleter方式并不一样,unique_ptr为了优化开销需要提供deleter的类型,这里仅讨论 unique_ptr。

struct FileCloserStruct {
	void operator()(FILE* fp) const {
		if (fp != nullptr) {
			fclose(fp);
		}
	}
};

void FileCloserFunc(FILE* fp) {
	if (fp != nullptr) {
		fclose(fp);
	}
}

auto FileCloserLambda = [](FILE* fp) {
	if (fp != nullptr) {
		fclose(fp);
	}
};

int main() {

	std::unique_ptr<FILE, FileCloserStruct> uptr1(fopen("test_file.txt", "w"));
	std::cout << sizeof(uptr1) << std::endl;// ???

	std::unique_ptr<FILE, void(*)(FILE*)> uptr2(fopen("test_file1.txt", "w"), FileCloserFunc);
	std::cout << sizeof(uptr2) << std::endl;// ???

	std::unique_ptr<FILE, std::function<void(FILE*)>> uptr3(fopen("test_file2.txt", "w"), FileCloserLambda);
	std::cout << sizeof(uptr3) << std::endl;// ???

	std::unique_ptr<FILE, decltype(FileCloserLambda)> uptr4(fopen("test_file3.txt", "w"), FileCloserLambda);
	std::cout << sizeof(uptr4) << std::endl;// ???

	return 0;
}

假设都是在MSVC 32位程序上测,

先来看第一个:

	std::unique_ptr<FILE, FileCloserStruct> uptr1(fopen("test_file.txt", "w"));
	std::cout << sizeof(uptr1) << std::endl;// 4

如果这里只使用了无状态的自定义deleter其实和raw pointer是一样大小。

稍微深入一点点这里是如何优化掉deleter大小的,以MSVC源码为例,其他思想上应该都是一样的

这个是unique_ptr内部使用_Compressed_pair来存deleter和pointer。

根据_Compressed_pair的实现可以看出来利用模板偏特化进行了判断,如果deleter是个空基类并且可以继承的话,就不需要保存这个deleter类型的成员,直接继承这个deleter类型用EBO(空基类优化)[1]从对象布局中优化掉。

第二个:

	std::unique_ptr<FILE, void(*)(FILE*)> uptr2(fopen("test_file1.txt", "w"), FileCloserFunc);
	std::cout << sizeof(uptr2) << std::endl;// 8

这里传入的是函数指针,阻止了EBO,需要额外的变量来保存,所以就是8了。

第三个:

	std::unique_ptr<FILE, std::function<void(FILE*)>> uptr3(fopen("test_file2.txt", "w"), FileCloserLambda);
	std::cout << sizeof(uptr3) << std::endl;// 48

这也是一开始抛出的问题,虽然具体数字和最上面博主测出来的不一致,但这也跟环境有关,此处不深究,重点是,它太大了!

因为std::function本来就不是lambda的原类型,std::function是通用多态函数封装器,非常强大,但是这种强大也是有代价的,直接使用sizeof(std::function<xxx>)看它也可以发现它是需要一定的内存开销的[2]。而把std::function作为类型传给了unqiue_ptr deleter时,等于在unique_ptr里把这个std::function也给存了起来,开销自然就大了...

第四个:

	std::unique_ptr<FILE, decltype(FileCloserLambda)> uptr4(fopen("test_file3.txt", "w"), FileCloserLambda);
	std::cout << sizeof(uptr4) << std::endl;//4

decltype直接获取lambda原类型了,同样可以进行EBO,所以也是原始指针大小。

总结:

unique_ptr自定义deleter其实用最朴素的结构体仿函数式写法就很稳了,如果想用lambda的话,请使用decltype。

参考

  1. ^https://en.cppreference.com/w/cpp/language/ebo
  2. ^https://stackoverflow.com/questions/13503511/sizeof-of-stdfunctionvoidint-type

编辑于 2021-04-24 20:02

C / C++

C++

编程语言

理性发言,友善互动

2 条评论

李华

FileCloserLambda其实是default constructable,但是c++17不允许
std::unique_ptr<FILE, decltype(FileCloserLambda)> uptr4(fopen("test_file3.txt", "w"));
也就是必须要有第二个参数FileCloserLambda。


即使在c++17里
std::unique_ptr<FILE, decltype(FileCloserLambda)> uptr4(fopen("test_file3.txt", "w"), FileCloserLambda);
FileCloserLambda也没存进unique_ptr里(size没有变大),都能EBO,为什么不能直接删掉第二个参数。


c++20里好像是允许了
std::unique_ptr<FILE, decltype(FileCloserLambda)> uptr4(fopen("test_file3.txt", "w"));

2022-05-09

Interlock

需要给lambda起一个名字,好麻烦

2022-07-15


http://www.kler.cn/a/429429.html

相关文章:

  • excel如何让单元格选中时显示提示信息?
  • Python毕业设计选题:基于django的民族服饰数据分析系统的设计与实现_hadoop+spider
  • Linux update-alternatives 命令详解
  • 首批|云轴科技ZStack成为信通院AI Cloud MSP技术服务实验室成员单位
  • C++11新特性之线程std::thread
  • JUC中的LockSupport工具类的使用下篇
  • 细说敏捷:敏捷四会之回顾会
  • mysql查询一对多重复数据拼接字符串
  • 【八股】HTTP
  • 数据挖掘之聚类分析
  • 网络安全中大数据和人工智能应用实践
  • Google BERT入门(3)Transformer的自注意力机制的理解(上)
  • 3D 生成重建023-DMV3D用扩散模型做3D生成大模型
  • Spring-AOP(面向切面)
  • 深入理解C#的TCPIP通信机制
  • 深度学习:CPU和GPU算力
  • Python基于OpenCV实现的人脸识别和笑容检测
  • 【Apache Paimon】-- 4 -- Flink 消费 kafka 数据,然后写入 paimon
  • Linux如何安装discuz
  • docker安装Emqx并使用自签名证书开启 SSL/TLS 连接