在阅读代码库时,我发现了一个解决常见问题的类 "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/