问题描述
我正在阅读Scott Meyers的《 Effective Modern C ++》,他正在讨论pimpl习惯用法,并使用unique_ptr
指向实现类,但是存在一个特殊的成员函数(例如析构函数)要求该类型的问题完整.这是因为unique_ptr
的默认删除程序会在使用delete p
之前静态断言要删除的类型是否完整.因此,必须在实现文件中定义类的任何特殊成员函数(而不是由编译器生成),而在实现类定义后 .
I'm reading Effective Modern C++ by Scott Meyers and he's discussing the use of the pimpl idiom and pointing to the implementation class with unique_ptr
, but there is an issue of special member functions (such as destructors) requiring the type to be complete. This is because unique_ptr
's default deleter statically asserts whether the type to be deleted is complete, before delete p
is used. So any special member functions of the class must be defined in the implementation file (rather than being compiler-generated), after the implementation class has been defined.
在本章的最后,他提到,如果使用的智能指针为shared_ptr
,则无需在实现文件中定义特殊的成员函数,这源于它支持自定义删除器的方式.引用:
At the end of the chapter, he mentions there is no need to define special member functions in the implementation file if the smart pointer used is shared_ptr
, and this stems from the way it supports a custom deleter. To quote:
尽管如此,我仍然看不到为什么shared_ptr
在类未完成的情况下仍然可以工作.似乎使用shared_ptr
时没有编译器错误的唯一原因是因为没有像unique_ptr
那样的静态断言,并且由于缺少断言而可能发生未定义的运行时行为.
Despite this, I still can't see why shared_ptr
could still work without the class being complete. It seems like the only reason there is no compiler error when using shared_ptr
is because there is no static assertion like unique_ptr
had, and that undefined runtime behaviour could instead occur because of this lack of assertion.
我不知道shared_ptr
的析构函数的实现,但是(通过阅读C ++ Primer)我得到的印象是它的工作原理类似于:
I don't know the implementation of the shared_ptr
's destructor, but (from reading C++ Primer) I gathered the impression it works something like:
del ? del(p) : delete p;
del
是自定义删除器的指针或函数对象. Cppreference 还可以清除shared_ptr
析构函数,而无需自定义删除器使用delete p
Where del
is a pointer or function object to the custom deleter. Cppreference also makes it clear the shared_ptr
destructor with no custom deleter uses delete p
重点强调已删除的类型必须完整的事实. pimpl惯用语的一个最小示例:
Emphasis on the fact that the deleted type must be complete. A minimal example of the pimpl idiom:
//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;
}
编译main.cpp
中的Widget a
时,将为Widget
类型(在main.cpp
之内)实例化shared_ptr
的模板,并且可能为shared_ptr
生成的已编译析构函数包含行,因为我没有提供自定义删除器.但是,此时仍未定义Impl
,但仍执行了行delete pImpl
.当然,这是不确定的行为吗?
When Widget a
in main.cpp
is compiled, the template of shared_ptr
is instantited for type Widget
(within main.cpp
) and presumably the resulting compiled destructor for shared_ptr
contains execution of the line delete pImpl
, because I have not supplied a custom deletor. However at that point, Impl
still has not been defined, yet the line delete pImpl
is executed. This, surely, is undefined behaviour?
那么在与shared_ptr
一起使用pimpl习惯用法时,我不必在实现文件中定义特殊的成员函数来避免未定义的行为呢?
So how is it that when using the pimpl idiom with shared_ptr
, I don't have to define the special member functions in the implementation file to avoid undefined behaviour?
推荐答案
在此处创建共享指针的删除器:
The deleter for a shared pointer is created here:
Widget::Widget(): pImpl(new Impl) {}
直到这一点,所有共享指针都具有与std::funciton<void(Impl*)>
相同的作用.
until that point, all the shared pointer has is the equivalent of a std::funciton<void(Impl*)>
.
当使用T*
构造shared_ptr
时,它将编写一个删除程序并将其存储在等效的std::function
中.那时类型必须是完整的.
When you construct a shared_ptr
with a T*
, it writes a deleter and stores it in the std::function
equivalent. At that point the type must be complete.
因此,在完全定义了Impl
之后,您需要定义的唯一函数是从某种T*
创建pImpl
的那些函数.
So the only functions you have to define after Impl
is fully defined are those that create a pImpl
from a T*
of some kind.
这篇关于使用shared_ptr处理不完整类型的Pimpl习惯用法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!