为什么以及何时需要提供我自己的删除器?关键字 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
}
最佳答案
当标准 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?
关于c++ - 为什么以及何时需要提供我自己的删除器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51278175/