c++ - 为什么以及何时需要提供我自己的删除器?

标签 c++ smart-pointers

为什么以及何时需要提供我自己的删除器?关键字 delete 还不够吗?

If you use a smart pointer to manage a resource other than memory allocated by new, remember to pass a deleter.


更新:

正如评论中所问的那样,我不清楚引用的文本和示例的原因是我想错了一些事情,我一直认为智能指针只是为动态内存管理而发明的/与动态内存管理相关.所以这个例子使用智能指针来管理一个非动态内存的东西让我很困惑。

一位前辈的好解释:

The smart pointer doesn't care at all about something being dynamic memory as such. It's just a way to keep track of something while you need it, and destroy that something when it goes out of scope. The point of mentioning file handles, network connections, etc., was to point out that they're not dynamic memory, but a smart pointer can manage them just fine anyway.


C++ Primer 5th以伪网络连接(不定义析构函数)来说明。

不好:

struct destination; // represents what we are connecting to
struct connection; // information needed to use the connection
connection connect(destination*); // open the connection
void disconnect(connection); // close the given connection
void f(destination &d /* other parameters */)
{
// get a connection; must remember to close it when done
connection c = connect(&d);
// use the connection
// if we forget to call disconnect before exiting f, there will be no way to closes
}

好:

void end_connection(connection *p) { disconnect(*p); }
void f(destination &d /* other parameters */)
{
connection c = connect(&d);
shared_ptr<connection> p(&c, end_connection);
// use the connection
// when f exits, even if by an exception, the connection will be properly closed
}

完整的上下文截图(我清除了一些不相关的文字):
Smart Pointers and Dumb classes

Smart Pointers and Dumb classes part 2

最佳答案

当标准 delete 不适用于释放、释放、丢弃或以其他方式处置其生命周期由智能指针控制的资源时,您需要为智能指针创建提供自己的删除。

智能指针的典型用途是将内存分配为由智能指针管理的资源,这样当智能指针超出范围时,被管理的资源(在本例中为内存)通过使用 delete 被丢弃运算符。

标准的 delete 操作符做了两件事:(1) 调用对象的析构函数以允许对象在其分配的内存被释放或解除分配之前执行它需要做的任何清理工作;(2) 释放由构造对象时的标准 new 运算符。这与 new 运算符发生的情况相反,该运算符 (1) 为对象分配内存并执行为对象建立构造环境所需的基本初始化,以及 (2) 调用对象的构造函数来创建对象的起始状态。见 What does the C++ new operator do other than allocation and a ctor call?

因此,需要您自己的删除器的关键问题是“在对象的析构函数完成后,在调用对象构造函数之前执行的哪些操作需要展开并退出?”

通常这是某种类型的内存分配,例如由标准 new 运算符完成。

但是,对于使用 new 运算符分配的内存以外的某些资源,使用 delete 运算符是不合适的,因为该资源不是使用 new 运算符分配的内存。

因此,当对这种 delete 运算符不合适的资源使用智能指针时,您需要提供自己的删除器方法或函数或运算符,当智能指针超出范围并触发它自己时将使用它们析构函数将依次处理丢弃由智能指针管理的任何资源。

一个简单的输出示例

我将一个简单的示例与 std::unique_ptr<> 以及生成的输出放在一起,以显示使用和不使用带有指针的删除器以及显式使用析构函数。

一个简单的 Windows 控制台应用程序的源代码如下所示:

// ConsoleSmartPointer.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <memory>
#include <string>
#include <iostream>

class Fred {
public:
    Fred() { std::cout << "  Fred Constructor called." << std::endl; }
    ~Fred() { std::cout << "  Fred Destructor called." << std::endl; }
};
class George {
public:
    George() { std::cout << "   George Constructor called" << std::endl; }
    ~George() { std::cout << "   George Destructor called" << std::endl; }
private:
    int iSomeData;
    std::string  a_label;
    Fred  myFred;
};

void cleanupGeorge(George *)
{
    // just write out a log and do not explicitly call the object destructor.
    std::cout << "  cleanupGeorge() called" << std::endl;
}

void cleanupGeorge2(George *x)
{
    // write out our message and then explicitly call the destructor for our
    // object that we are the deleter for.
    std::cout << "  cleanupGeorge2() called" << std::endl;
    x->~George();    // explicitly call destructor to do cleanup.
}

int func1()
{
    // create a unique_ptr<> that does not have a deleter.
    std::cout << "func1 start. No deleter." << std::endl;

    std::unique_ptr<George> p(new George);

    std::cout << "func1 end." << std::endl;
    return 0;
}

int func2()
{
    // create a unique_ptr<> with a deleter that will not explicitly call the destructor of the
    // object created.
    std::cout << "func2 start. Special deleter, no explicit destructor call." << std::endl;

    std::unique_ptr<George, void(*)(George *)> p(new George, cleanupGeorge);

    std::cout << "func2 end." << std::endl;
    return 0;
}

int func3()
{
    // create a unique_ptr<> with a deleter that will trigger the destructor of the
    // object created.
    std::cout << "func3 start. Special deleter, explicit destructor call in deleter." << std::endl;

    std::unique_ptr<George, void(*)(George *)> p(new George, cleanupGeorge2);

    std::cout << "func3 end." << std::endl;
    return 0;
}

int main()
{
    func1();
    func2();
    func3();
    return 0;
}

上面的简单应用程序生成以下输出:

func1 start. No deleter.
  Fred Constructor called.
   George Constructor called
func1 end.
   George Destructor called
  Fred Destructor called.
func2 start. Special deleter, no explicit destructor call.
  Fred Constructor called.
   George Constructor called
func2 end.
  cleanupGeorge() called
func3 start. Special deleter, explicit destructor call in deleter.
  Fred Constructor called.
   George Constructor called
func3 end.
  cleanupGeorge2() called
   George Destructor called
  Fred Destructor called.

其他帖子

What is a smart pointer and when should I use one?

Using custom deleter with std::shared_ptr

另请参阅有关 std::make_shared<> 的删除器以及为什么它不可用的讨论。 How to pass deleter to make_shared?

Is custom deleter for std::unique_ptr a valid place for manual call to destructor?

When does std::unique_ptr<A> need a special deleter if A has a destructor?

RAII and smart pointers in C++

关于c++ - 为什么以及何时需要提供我自己的删除器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51278175/

相关文章:

c++ - C++找不到成员

c++ - Windows WMI 对 MacOS C++ 开发的等价物是什么?

c++ - 通过引用传递 vector 的尾递归

c++ - 如何对 vector 中的字符串进行模式匹配?

c++ - 唯一指针和 const 正确性

c++ - 使用智能指针管理缓冲区

C++智能指针循环链接

c++ - 发送任意(原始)数据包

c++ - 如何使用 lambda 作为 std::unique_ptr 的删除器?

c++ - 如何 std::bind 智能指针返回方法?