通过传递外部指针或内部调用 new 构造的类,我可以在销毁时天真地删除这个指针吗?

Class constructed by passing an external pointer or by internally calling new, can I naively delete this pointer on destruction?

本文关键字:指针 我可以 天真 删除 外部 内部 调用 new      更新时间:2023-10-16

我有类A,它有一个指向B类的指针作为其字段之一:

class A {
private:
    B *ptr;
public:
    A(B*);
    A();
    ~A();
}
A::A(B *ptr)
{
    this->ptr = ptr;
}
A::A()
{
    this->ptr = new B();
}
A::~A()
{
    delete this->ptr;
}

我希望析构函数仅在 new 分配时才删除 B,我只能通过设置一个存储指针原点的标志来做到这一点(因此只有在构造 A 期间分配了 ptr 时才调用 delete),或者是否有更优雅的解决方案,即,我可以在任何指针上调用 delete(我知道如果传入的指针是使用 new 分配的, 它也将被删除,但如果指针是从调用 A 构造函数的作用域传入的,显然删除它是未定义的)?

更优雅的解决方案是不要拥有具有不同所有权模型的类,在这种情况下,拥有与不属于您的类。 不要将这两种想法混合在同一个类类型中,因为它只会混淆您的用户/客户端(通过违反最小意外原则)。

要么

创建具有所需不同行为的单独类,要么选择一个所有权模型并强制客户端坚持使用它(要么在非默认构造函数中传入并转移所有权,要么不允许自我分配)。

还是有更优雅的解决方案

不,不是真的;需要清楚地记录谁拥有这个指针。 看起来你的班级是这样,所以你能自己构建ptr吗?

#include <memory>
class A {
private:
    std::unique_ptr<B> ptr;
public:
    A(some_data args) 
        : ptr(new B(args)) { };
    A();    
}

如果你的类不拥有指针,那也没关系,只是不要释放它。 否则,您将需要在外部(即dealloc_on_destruct)设置一个标志来知道该怎么做,这可能会变得混乱。

仅使用您提供的代码无法回答该问题。真正的问题是为什么你可以接受指针,如果指针传递给你,谁拥有内存。

根据对象在特定域中应执行的操作,可以采用不同的替代方法。例如,如果您可以与调用者共享所有权,那么您应该采取shared_ptr,如果您将所有权保留在对象中,则应使用unique_ptr在界面中明确表示您的对象将获取所有权。如果不共享所有权,请考虑传递对对象的引用(即使在内部存储指针),在这种情况下,您可以将所有权与实际使用分开:

// option 1: shared ownwership
class A {
   std::shared_ptr<B> ptr;
public:
   A( std::shared_ptr<B> const & p ) : ptr(p) {}
   A() : ptr( std::make_shared<B>() ) {}
};
// option 2: grab ownership
class A {
   std::unique_ptr<B> ptr;
public:
   A( std::unique_ptr<B> p ) : ptr( std::move(p) ) {}
   A() : ptr( new B(); }
};
// option 3: no ownership (unless we create the object)
// (separating concerns: ref to access the object, ptr to manage it)
class A {
   std::unique_ptr<B> ptr;   // only ownership
   B * ref;
public:
   A( B & b ) : ptr(), ref(&b) {}
   A() : ptr( new B() ), ref(ptr.get()) {}
   // Internally use 'ref' to access the object the destructor will delete it 
   // if needed
};

除了其他人所说的(避免这种设计):如果你绝对无法避免这种设计,并且你需要这个有趣的类,它有时拥有它的B,有时不拥有,这取决于调用哪个构造函数,那么你可以像这样使用unique_ptr(或shared_ptr

):
void delete_it(B*) { std::default_delete<B>()(t); }
void do_nothing(B *t) {}
class A {
    unique_ptr<B, void(*)(B*)> ptr;
  public:
    A(B *ptr) ptr(ptr, do_nothing) {}
    A() ptr(new B(), delete_it) {}
    // no need for destructor
    // hence also no need to worry about copy constructor or assignment
};

它比"原始指针加标志"更好的原因是,您不必实现删除器和副本(并在 C++11 中移动)。在性能方面,它可能比"原始指针加标志"更糟糕,但大多数时候你不需要担心这一点。如果合理的话,你可以回来做额外的工作。您无需更改类的源接口即可切换,尽管这可能会破坏二进制兼容性。

相关文章: