c++ - 使用 lambda 删除器返回唯一指针时可能出现悬空引用

标签 c++

在阅读代码库时,我发现了一个解决常见问题的类 "requiring objects on heap"有一个 protected 析构函数会出现问题。该类也是抽象的,允许通过静态函数创建单个实例方法。

您可以看到下面模式的简化版本。我对 ::Make 的返回类型有疑问:

 // header file --------------------------------------
 namespace ooo {

 class Object {
 public:
   Object() { std::cout << "Constructor call\n"; }
   virtual void F() = 0;

   static std::unique_ptr<Object, void (*)(Object *)> Make();

 protected:
   virtual ~Object() { std::cout << "Destructor call\n"; }
 };

 } // namespace ooo

 // implementation file ----------------------------
 namespace {

 class ObjectImpl : public ooo::Object {
 public:
   void F() override { std::cout << "doing stuff\n"; }
   ~ObjectImpl() override { std::cout << "destructor of derived\n"; }
 };

 } // namespace

 namespace ooo {

 std::unique_ptr<Object, void (*)(Object *)> Object::Make() {
   return std::unique_ptr<Object, void (*)(Object *)>{
       new ObjectImpl, [](Object *e) { delete e; }}; 
 }

 } // namespace ooo

 void Use() {
   // The design of class Object, should prevent users from doing the following:
   //  
   // 1. Explicit allocation - creating instance in free store:
   // std::unique_ptr<Envelope> ptr{new Envelope}; // compilation error due to
   // private destructor.
   //  
   // 2. Plain automatic instance:
   // Envelope e; // Compilation error due to private destructor.
   //  
   // 3. Plain free store instance:
   // auto ee = new ooo::Object; // You can't have an instance of abstract class.

   // This is the use pattern we want to enable
   auto e = ooo::Object::Make();
 }

 int main() {
   std::cout << "Entering example\n\n";
   Use();

   return 0;
 }

如您所见,::Make()返回一个唯一指针,其类型必须显式声明(我们不能使用自动返回类型,因为用户只需要包含 header )。

由于删除器的类型是指向函数的指针,并且我们使用 lambda 作为删除器,那么唯一指针最后不会捕获指向垂死对象的指针吗?

作为旁注,我相信使用 lambda 是因为由于作用域(它在静态函数内声明),它可以访问 protected 析构函数。

最佳答案

不会有悬空引用。 lambda 捕获由启动 lambda 的 [] 中的内容指定。在这里,它是空的,这意味着它没有捕获任何东西。因此,这个 lambda 相当于一个独立(或静态成员)函数。

代码可以这样编写以获得相同的效果,但不太简洁:

 // static
 void Object::Destroy( Object* e ) { delete e; }

 std::unique_ptr<Object, void (*)(Object *)> Object::Make() {
   return std::unique_ptr<Object, void (*)(Object *)>{
       new ObjectImpl, Destroy }; 
 }

上观看直播 Coliru

虽然该类的用户如果决心以错误的方式做事,可以找到滥用系统的方法(例如,通过调用 unique_ptr::get_deleter()),但这里的尝试是遵循 Scott Meyers 在《Effective C++》中的格言:“让接口(interface)易于正确使用,而难以错误使用。”

关于c++ - 使用 lambda 删除器返回唯一指针时可能出现悬空引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59861418/

相关文章:

c++ - main() 在哪里返回它的值?

c++ - 再次运行 boolean 控制循环

C++ 缓冲区溢出

c++ - 基于模板的 switch 语句的 if constexpr

c++ - boost::asio async_read_some 示例代码未读取套接字中的所有数据

c++ - 从文本文件读取到数组

c++ - 使用 on_<widget>_<signal> where widget it own class

java - 将 .const 文件 (c++) 转换为 .java 类时出现问题

c++ - 使控制台输出 "verbose"或不输出的最快方法

c++ - 如何在 C++ 中访问派生类中的变量?