我有一个方法,它有一些指针作为参数。可以使用来自被调用者的命名指针调用此方法,也可以动态创建指向新对象的指针,并在调用方法时直接将其作为参数传递。
myClass *myPtr = new myClass(...);
myMethod(myPtr);
VERUS
myMethod(new myClass(...));
问题是,如果这两个都是有效选项,那么如何正确释放传入的指针?如果在程序中再次访问myPtr,则删除myMethod中的myPtr将导致崩溃。如果我不删除myPtr,第二个选项将导致内存泄漏(如果使用它)。使用这两个选项都有好处,因此两者都不应该破坏程序。
除了使用STL之外,这个问题有哪些解决方案?我是否必须实施自己的垃圾收集器?
答案 0 :(得分:8)
我想说,在这种情况下,调用者应该负责释放对象。您可以考虑各种选项,最简单的是:
myClass myInstance = myClass; // or myClass(arg1, arg2, ...)
// and the pass it to your method like this:
myMethod(&myInstance);
您还可以考虑一些智能指针选项,例如std::tr1::shared_ptr
或来自boost的内容。
UPDATE:如果您的方法应该能够将NULL
- 指针作为其参数,那么根本没有问题:
// this is your method declaration:
void myMethod(const myClass *myPtr);
// in your tests or wherever in your code you can call it like
myClass myInstance = myClass; // or myClass(arg1, arg2, ...)
myMethod(&myInstance);
// or like this:
myMethod(NULL);
// for as long as your method has something like this in it:
if (myPtr)
myPtr->someMethod();
答案 1 :(得分:3)
您可以使用智能指针,例如来自boost的shared_ptr。
如果不是,您需要清楚说明拥有对象的人。如果您要接管所有权或将其留给来电者
如果你将它留给调用者,使用function(new whatever())
形式不是一个好主意,但泄密将由调用者负责。
如果您打算接管所有权,创建一个接收器方法,选择一个合适的名称将是个好主意,当然您需要在完成后自行删除这些对象
答案 2 :(得分:2)
首先,您需要分配对象的所有权:您需要有一个一致的策略(并强制执行!)“谁”有权删除对象。一旦明确确定,您就不应该遇到问题。
一些策略:
租赁对象:贷方保留所有权
提供对象:转让所有权
其次,对于对象的跟踪使用,您需要一个基础设施,例如“智能指针”。在这里,您有两个需要关注的类别:
对象是“单独引用”,即只有一个“用户”
对象是“多参考”,即在某个时间点有多个“用户”
对于(1),“跟踪信息”是指针本身,而在(2)中,您需要更多的基础设施。
答案 3 :(得分:2)
这是关于所有权的一切。
您需要决定谁拥有所有权(或者是否拥有共享权) 基本上传递指针是一个非常糟糕的想法和 NOT 非常C ++(这基本上是一个C接口,因为没有所有权的概念)。在C ++程序中,您应该以非常明确的所有权转移感来定义您的接口(函数)。
你有几个选择。
在你的情况下,我会根据情况建议1或2 很明显,该函数无法删除指针,因为它有时可能没有所有权。因此,所有权仍然在呼叫者的一边。因此调用者必须适当地调用delete。希望通过一些智能指针机制。
答案 4 :(得分:1)
你应该使用某种智能指针
对于这个简单的情况,auto_ptr
很好,但一般情况下你应该使用scoped_ptr
或shared_ptr
。
如果您对STL或boost有一些无法解释的恐惧症,您可以相对轻松地拉出自己的智能指针类(如果您不需要超级异常安全)。
答案 5 :(得分:0)
您需要决定所有权:
来电者拥有对象:对象是LOANED才能运行,来电者负责分配和删除。
函数拥有对象:对象为DONATED to function,Caller负责分配,被调用者(函数)负责删除。
通过共享指针使用引用计数来避免此问题。
(2是丑陋的,因为它意味着不同的代码负责分配和删除,但这可能是您的情况的正确解决方案 - 它还意味着如果调用者想要保留对象,则创建参数的副本)。
答案 6 :(得分:0)
这两个都是c ++语法意义上的有效选项,但我认为第二个选项几乎在所有情况下都是糟糕的软件设计,并且在一个函数中允许两个选项甚至可能在所有情况下都是糟糕的软件设计。
要么调用类创建对象,调用方法并删除对象,
或调用类创建对象,调用方法,并期望方法删除对象。
我认为在一个方法调用中为什么两个选项都应该被认为是合理的没有充分的理由。从文档或注释中可以清楚地看出该方法使用的版本,然后由开发人员使用该方法,或者承担后果。
答案 7 :(得分:0)
常见的行为是既不创建也不破坏对象的函数。但是,有功能。常见的术语是“源和汇”。如果您没有使用更多功能的智能指针,那么一个非常有用的约定是使用std::auto_ptr<T>
来源和接收器。 “source”函数返回创建为auto_ptr<T>
的对象,而sink接收类型为auto_ptr<T>
的参数。
好处是,即使您忘记了源函数的返回值,也不会有内存泄漏。在语句结束时,返回的auto_ptr会销毁返回的对象。同样地,很明显传入void sink(auto_ptr<T> unreferenced) { }
的参数将在sink
返回之前被销毁。
答案 8 :(得分:0)
内存应该由其所有者释放。通常,所有者是分配内存的实体,但在某些情况下,您可以允许转让所有权。
void foo() {
myClass* p = new myClass(); // foo owns the pointer, so foo should release it again
}
void foo() {
boost::shared_ptr<myClass> p = new myClass(); // foo allocates the memory, but *transfers* ownership to the smart pointer. The smart pointer is now responsible for freeing the object. (This is true for all types of smart pointers, including boost::scoped_ptr, std::auto_ptr or the C++0x std::unique_ptr.
}
void foo() {
ScopedMyClass x; // ScopedMyClass is some class wrapper which *internally* calls `new myClass`, and so owns the allocation and is responsible for freeing it before the end of the wrapper class' lifetime.
}
在具体情况下,如果myMethod
没有分配内存,那么它也不应该释放内存。
至于如何处理:
myMethod(new myClass(...));
只是不要那样做。如果您分配内存,则需要拥有它。首先存储指针,以便删除内存:
myClass* p = new myClass(...);
myMethod(p);
delete p;
甚至更好,*不首先动态分配对象,消除了所有权问题:
myMethod(myClass(...));
答案 9 :(得分:0)
我还不能发表评论但是..虽然我通常会强烈争辩'创造者拥有它'范式(你能真正证明其他任何正确吗?),我认为坚持拥有
myMethod(new myClass(...));
用法几乎要求转让所有权。也许谨慎地问一下你用这种用法试图完成什么,是否可以用另一种方式处理?就像一个引用临时而不是指针的重载?
myMethod(myClass& a) { return myMethod(&a); }
myMethod(myClass(...))
答案 10 :(得分:0)
好吧,我想我会把我的小石头加到大厦里......虽然我不确定它是否会被阅读。
这是内存的所有权问题,你的界面的作用是传达这样的含义:“这种方法是否取得所有权”。
为了更好地表达所有权,使用原始指针通常是不好的。 STL有一个混蛋auto_ptr
更适合这项任务(我正在等待即将到来的unique_ptr
相当不耐烦)。
对于方法,有几种方法可以接受参数:
// Value
void method(T);
// Reference
void method(T&);
// Pointer
void method(T*);
// Smart Pointer
void method(std::auto_ptr<T>);
我跳过了cv-qualification位,因为它与那里无关。
关键是,在这4个解决方案中,意图很明确:
auto_ptr
该方法获取内存的所有权,这意味着调用者之后无法使用该变量
std :: auto_ptr myT = std :: auto_ptr(new T()); 方法(MYT); 断言(myT.get()== 0); // myT不再保留任何内容了!
当然,这就是为什么auto_ptr
是野兽的原因,因为它不遵守复制对象使复制对象显然保持不变的惯例:通常所有公共方法都应该给出相同的结果如果你公开它,引用计数就是一个极端情况。
因此,如果调用者是否应该期望该方法取得所有权,那么您的接口应该更清楚,一种简单的方法是使用重载。
void method(T*); // do something
void method(std::auto_ptr<T> p)
{
method(p.get());
}
很简单,你现在清楚谁处理了记忆!
使用相同的推理,您也可以使用此重载技巧自动检查指针是否为null。
void method(T&); // do something
void method(T* p)
{
if (p) method(*p); else throw NullPointer("method");
}
但是我会避免滥用这个技巧,你最终会得到数以千计的方法。
所以请记住:所有权语义最好用CODE 表示,而不是在评论中表达。
您将立即与使用RAII管理资源混合使用,这意味着您永远不会将内存分配给原始指针:使用表示资源所有权的智能指针,以及指向您不拥有的现有对象的原始指针(可能为null,否则引用更好;)。