Ser*_*uin 5 c++ pimpl-idiom smart-pointers c++11
我正在阅读Scott Meyers撰写的Effective Modern C++,他正在讨论使用pimpl习语并指向实现类unique_ptr
,但是存在需要完整类型的特殊成员函数(例如析构函数)的问题.这是因为unique_ptr
在delete p
使用之前,默认删除器静态断言要删除的类型是否完整.所以任何类特殊的成员函数必须在实现文件中定义(而不是编译器生成的),之后实现类已定义.
在本章的最后,他提到如果使用的是智能指针,则不需要在实现文件中定义特殊的成员函数shared_ptr
,这源于它支持自定义删除器的方式.报价:
pImpl指针的std :: unique_ptr和std :: shared_ptr之间的行为差异源于这些智能指针支持自定义删除器的不同方式.对于std :: unique_ptr,删除器的类型是智能指针类型的一部分,这使编译器可以生成更小的运行时数据结构和更快的运行时代码.这种更高效率的结果是,当使用编译器生成的特殊函数(例如,析构函数或移动操作)时,指向类型必须是完整的.对于std :: shared_ptr,删除器的类型不是智能指针类型的一部分.这需要更大的运行时数据结构和稍慢的代码,但是当使用编译器生成的特殊函数时,指向的类型不需要完整.
尽管如此,我仍然不明白为什么shared_ptr
没有完成课程仍然可以工作.这似乎是使用时没有编译器错误的唯一原因shared_ptr
是因为没有像has那样的静态断言unique_ptr
,并且由于缺少断言而可能会发生未定义的运行时行为.
我不知道shared_ptr
析构函数的实现,但是(从阅读C++ Primer)我收集的印象它的工作原理如下:
del ? del(p) : delete p;
Run Code Online (Sandbox Code Playgroud)
del
自定义删除器的指针或函数对象在哪里.Cppreference还清楚地说明shared_ptr
了没有自定义删除器使用的析构函数delete p
3)
delete ptr
如果T
不是数组类型,则使用delete-expression ; .... Y必须是完整的类型.删除表达式必须格式正确,具有明确定义的行为并且不会抛出任何异常.
强调删除类型必须完整的事实.pimpl习语的最小例子:
//widget.h
#ifndef WIDGET
#define WIDGET
#include <memory>
class Widget{
public:
Widget();
private:
struct Impl;
std::shared_ptr<Impl> pImpl;
};
#endif // WIDGET
//widget.cpp
#include <string>
#include "Widget.h"
struct Widget::Impl{
std::string name;
};
Widget::Widget(): pImpl(new Impl) {}
//main.cpp
#include <iostream>
#include "Widget.h"
int main(){
Widget a;
}
Run Code Online (Sandbox Code Playgroud)
当Widget a
in main.cpp
编译时,模板shared_ptr
是为类型Widget
(内部main.cpp
)即时输出的,并且可能是由此产生的编译析构函数shared_ptr
包含行的执行delete pImpl
,因为我没有提供自定义deletor.但是,此时Impl
仍未定义,但该行delete pImpl
已执行.这肯定是未定义的行为?
那么在使用pimpl习语时怎么样呢?shared_ptr
我不必在实现文件中定义特殊的成员函数来避免未定义的行为?
此处创建共享指针的删除器:
Widget::Widget(): pImpl(new Impl) {}
Run Code Online (Sandbox Code Playgroud)
直到那一点,所有共享指针都相当于a std::funciton<void(Impl*)>
.
shared_ptr
使用a 构造a 时T*
,它会写一个删除器并将其存储在std::function
等效项中.此时,类型必须完整.
所以你Impl
完全定义的唯一函数是那些pImpl
从T*
某种类型创建的函数.
归档时间: |
|
查看次数: |
1340 次 |
最近记录: |