【问题标题】:Is there a way to force an object to be created on the heap with shared_ptr?有没有办法强制使用 shared_ptr 在堆上创建对象?
【发布时间】:2013-08-04 16:46:59
【问题描述】:

我想知道是否可以通过创建私有/受保护的析构函数并同时使用 shared_ptrs 来确保自动资源管理(shared_ptr 的 RAII 特性)来强制在堆上创建对象。 可以以不同的方式完成吗? 我问这个的原因是因为我在 STL 听到的(还没有看到)没有虚拟析构函数,所以除了...shared_ptr 之外没有其他方法可以确保安全销毁? 如果是这样,由于 shared_ptr 正在尝试访问析构函数,因此无法强制对象进入堆。 无论如何绕过这些限制?

【问题讨论】:

  • 我不明白你提出这个问题的理由。你能试着更准确地解释你想要什么吗?
  • 好吧,让我们谈谈一个假设的例子,假设我想创建一个类树并强制这些类的任何用户只在堆上创建对象。因此,只需将描述符设为私有/受保护即可。现在,如果对象超出范围,它就会被销毁,但如果有一个类树并且析构函数不是虚拟的,它就不会被很好地销毁。所以假设我不能使用 virtual 出于某种原因,我如何正确地销毁这些对象?
  • @LifePhilPsyPro 这听起来像是一个错误的问题。 不要使用virtual,因为这是正确的解决方案。或者,不使用基类指针的继承,那么你就不需要虚拟析构函数。
  • @LifePhilPsyPro:等等,如果我理解正确,你的类是从std::vector 这样的 STL 类派生的吗?这是这些问题的根源吗?
  • 嗯,实际上没有像理论问题那样多的“实际”问题。我试图弄清楚我能做什么,不能用 c++ 为我提供什么。所以我实际上并没有遇到我必须使用我描述的东西的情况。我只是在考虑如何将智能指针同时用于 RAII 和强制堆分配。我不知道何时以及为什么我可能需要它。但我认为这很有用。

标签: c++ c++11 stl heap-memory


【解决方案1】:

C++ 是一种将代码的正确性交给程序员的语言。试图通过一些复杂的方法来改变它通常会导致代码难以使用或不能很好地工作。强迫程序员的手,以便(s)他必须在堆上创建一个对象,即使对于那种特定情况来说这不是“正确的”也是很糟糕的。如果他愿意,让程序员射他/她自己的脚。

在较大的项目中,代码应由同行(最好至少有时由更高级的员工)审查,以确保其正确性并遵循项目的编码指南。

我不完全确定“虚拟析构函数”与“安全销毁”和“共享指针”之间的关系——这三个不同的概念并不密切相关——当类用作基础时,需要虚拟析构函数-class 派生一个新类。 STL 对象并不意味着派生自[通常,您使用模板或继承,尽管它们可以组合,但当您这样做时它会很快变得非常复杂],因此无需在 STL 中使用虚拟析构函数。

如果您有一个作为基类的类,并且存储是基于对基类的指针或引用完成的,那么您必须有虚拟析构函数 - 或者不要使用继承。

“安全销毁”,我认为,意味着“没有内存泄漏”[而不是“正确的销毁”,这当然也可能是一个问题 - 并导致内存泄漏问题]。对于大量情况,这意味着“首先不要使用指向对象的指针”。我在 SO 上看到了很多例子,程序员毫无理由地调用newvector<X>* v = new vector<X>; 绝对是“难闻的气味”(就像鱼或肉一样,如果气味难闻,则代码有问题)。如果您正在调用 new,那么使用共享指针、唯一指针或其他一些“包装”是个好主意。但是你不应该强迫这个概念 - 偶尔有很好的理由不这样做。

“共享指针”是“对象不再使用时自动销毁”的概念,是避免内存泄漏的有用技术。

既然我已经告诉过你不要这样做,这里有一种方法来实现它:

class X
{
    private:
       int x; 
       X() : x(42) {}; 
    public:
       static shared_ptr<X> makeX() { return make_shared<X>(); }
};

由于构造函数是私有的,类的“用户”不能调用“new”或创建这种对象。 [您可能还想将复制构造函数和赋值运算符放在私有或使用delete 以防止它们被使用]。

但是,我仍然认为首先这是一个坏主意。

【讨论】:

  • 如果您只使用共享指针管理对象,则实际上不需要虚拟析构函数。只要第一个共享指针使用指向最派生类型的指针进行初始化(按照您的示例,它将是),删除器就会做正确的事情。
  • 在某些情况下可能是可行的,例如如果类派生自std::enable_shared_from_this
  • make_shared 调用将失败,因为 make_shared 无权访问私有构造函数。这种模式不受欢迎的另一个原因。
  • 您可以通过添加一个公共构造函数来解决此问题,该构造函数需要一个只有 X 才能构造的令牌,并将其传递给 make_shared
【解决方案2】:

Mats 的回答确实是错误的。 make_shared 需要一个公共构造函数。 但是,以下内容是有效的:

class X
{
private:
    int x;

    X() : x( 42 ) {};
public:

    static std::shared_ptr<X> makeX()
    {
        return std::shared_ptr<X>( new X() );
    }
};

我不喜欢使用new 关键字,但在这种情况下,这是唯一的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-15
    • 2016-07-06
    • 1970-01-01
    • 1970-01-01
    • 2011-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多