2017-10-06 82 views
2

我有一个班级,他的constructor将一个对象的地址作为参数。检查内存是否在堆上?

MyClass(OtherClass * otherClass); 

在这个类的Destructor我尝试deleteOtherClass实例。

~MyClass() { 
    if(otherClass != nullptr) { 
     delete otherClass; 
    } 
} 

时遇到的问题是,当我把这种constructor我称之为从stack而不是从heap的元素,从而如下面我叫它:

MyClass的MyClass的(& otherObject );

因此,当myClass对象超出范围时,我得到一个异常。如果我的OtherObject变量是在stackheap上声明的,我该如何推测?换句话说,我怎么知道我是否可以delete这个对象?

+3

如果你没有任何物体所有权规则,代码将出现混乱,你将有这样的问题。通常传递一个指针是“给所有权”该对象。如果你不想这样做,你需要将其改为参考。 – tadman

+2

你应该把它作为你的类合同的一个明确部分,不管它是否“获得了所有权”的提供的参数,然后让代码创建负责指定它的对象 –

+1

考虑使用'shared_ptr ',尽管这意味着你永远不会有一个“堆栈对象”给它。在安全性方面,这是一件好事 –

回答

5

尽管有系统特定的方法可能能够判断内存是堆栈还是堆栈,但实际上并没有什么帮助:您可能有一个指向堆中另一个对象成员的指针。内存将在堆上,但您仍然不负责删除该对象。换句话说:做不是走上你的路!

解决该问题的正确方法是使界面中的所有权语义清晰明了,并与此相符。基本上有两个方向可以走:

  1. 你的类可以故意接管传入构造指针责任!相反,班级用户有责任处理这些对象以确保它们在存在对象时有效。如果您可以获取堆栈(或成员)对象的指针,则用户必须保证这些对象的有效性,对于其他方式,对于用户来处理它们可能是完全微不足道的,例如通过管理它们std::unique_ptr<OtherClass>
  2. 您的课程承担责任全部对象通过,它将delete所有这些。它成为调用者的责任,而不是而不是传递指向其他地方管理的对象的指针,例如指向堆栈或成员对象上的对象。

有一种混合的方法,你的班级有时对对象负责,但并非总是如此。但是,这种方法的实现实际上是上述两种方法的组合:您将采用合适的智能指针作为构造函数参数,并且用户有责任确保智能指针由您的用户类。例如,你的班级可能需要一个std::shared_ptr<OtherClass>,其中正常的施工将是delete的对象。当用户想要传递指向另外拥有的对象的指针时,std::shared_ptr<OtherClass>将被构造成具有删除器,其不是而是delete指针。下面是一个简单的程序展示了两种不同的管理策略std::shared_ptr

#include <iostream> 
#include <memory> 

struct foo { 
    char const* name; 
    foo(char const* name) 
     : name(name) { 
     std::cout << "foo::foo(" << name << "): " << this << "\n"; 
    } 
    ~foo() { 
     std::cout << "foo::~foo(" << name << "): " << this << "\n"; 
    } 
}; 

int main() { 
    std::shared_ptr<foo>(new foo("heap")); 
    foo f("stack"); 
    std::shared_ptr<foo>(&f, [](auto){}); 
} 
+0

通过系统特定的方法,你可能指的是找到值堆栈指针(esp或rsp)和堆栈底部(esp + offset)。然后你比较这个值并检查它是否在堆栈范围内。但请记住,如果您这样做,您的代码将无法移植。 – Asesh

+0

@Asesh:是的,确实如此:虽然我知道在大多数系统中[当前(*)]可以指示指针是指堆还是栈,但我明显推荐*不*使用这种方法。 (*)如果/当[stackful]协程变得可用时,将很难判断指针是指向堆还是栈,因为协程栈会来自堆。然而,这只是*不利用对象位置的另一个原因[用于决定是否删除'd')。 –