gpt4 book ai didi

c++ - 使用 shared_ptr 处理不完整类型的 Pimpl 习语

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:18:59 28 4
gpt4 key购买 nike

我正在阅读 Scott Meyers 的 Effective Modern C++,他正在讨论 pimpl 习语的使用,并使用 unique_ptr 指向实现类,但存在特殊成员函数的问题(例如析构函数)要求类型完整。这是因为 unique_ptr 的默认删除器在使用 delete p 之前静态断言要删除的类型是否完整。因此类的任何特殊成员函数都必须在实现文件中定义(而不是由编译器生成),实现类定义之后。

在章节的最后,他提到如果使用的智能指针是shared_ptr,就不需要在实现文件中定义特殊的成员函数,这源于它支持自定义的方式删除器。引用:

The difference in behavior between std::unique_ptr and std::shared_ptr for pImpl pointers stems from the differing ways these smart pointers support custom deleters. For std::unique_ptr, the type of the deleter is part of the type of the smart pointer, and this makes it possible for compilers to generate smaller runtime data structures and faster runtime code. A consequence of this greater efficiency is that pointed-to types must be complete when compiler-generated special functions (e.g., destructors or move operations) are used. For std::shared_ptr, the type of the deleter is not part of the type of the smart pointer. This necessitates larger runtime data structures and somewhat slower code, but pointed-to types need not be complete when compiler-generated special functions are employed.

尽管如此,我仍然不明白为什么 shared_ptr 在类没有完成的情况下仍然可以工作。使用 shared_ptr 时没有编译器错误的唯一原因似乎是因为没有像 unique_ptr 那样的静态断言,因此可能会出现未定义的运行时行为缺乏主张。

我不知道 shared_ptr 的析构函数的实现,但是(通过阅读 C++ Primer)我得到的印象是它的工作方式类似于:

del ? del(p) : delete p;

其中 del 是指向自定义删除器的指针或函数对象。 Cppreference还明确了没有自定义删除器的 shared_ptr 析构函数使用 delete p

3) Uses the delete-expression delete ptr if T is not an array type; .... Y must be a complete type. The delete expression must be well formed, have well-defined behavior and not throw any exceptions.

强调删除的类型必须是完整的。 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;
}

编译main.cpp中的Widget a时,shared_ptr的模板被Widget实例化(在 main.cpp 中)并且可能为 shared_ptr 生成的编译析构函数包含执行 delete pImpl 行,因为我没有提供自定义删除器。然而此时,Impl 仍未定义,但 delete pImpl 行已被执行。这肯定是未定义的行为?

那么,当使用带有 shared_ptr 的 pimpl 惯用语时,我不必在实现文件中定义特殊成员函数以避免未定义的行为,这是怎么回事?

最佳答案

共享指针的删除器在此处创建:

Widget::Widget(): pImpl(new Impl) {}

直到那时,所有共享指针都相当于一个std::funciton<void(Impl*)> .

当你构造一个 shared_ptr 时用T* ,它写了一个删除器并将其存储在 std::function 中相等的。那时类型必须是完整的。

因此您必须在 Impl 之后定义的唯一函数完全定义的是创建 pImpl 的那些来自T*某种形式。

关于c++ - 使用 shared_ptr 处理不完整类型的 Pimpl 习语,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40619984/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com