在 C++ 中返回对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/204396/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Returning Objects in C++
提问by bibstha
When returning objects from a class, when is the right time to release the memory?
从类返回对象时,什么时候释放内存合适?
Example,
例子,
class AnimalLister
{
public:
Animal* getNewAnimal()
{
Animal* animal1 = new Animal();
return animal1;
}
}
If i create an instance of Animal Lister and get Animal reference from it, then where am i supposed to delete it?
如果我创建一个 Animal Lister 实例并从中获取 Animal 引用,那么我应该在哪里删除它?
int main() {
AnimalLister al;
Animal *a1, *a2;
a1 = al.getNewAnimal();
a2 = al.getNewAnimal();
}
The problem here is AnimalLister doesnot have a way to track the list of Animals Created, so how do i change the logic of such code to have a way to delete the objects created.
这里的问题是 AnimalLister 没有办法跟踪创建的动物列表,所以我如何更改此类代码的逻辑以删除创建的对象。
回答by Eclipse
Depending on your usage, there are a couple of options you could go with here:
根据您的使用情况,您可以选择以下几个选项:
Make a copy every time you create an animal:
class AnimalLister { public: Animal getNewAnimal() { return Animal(); } }; int main() { AnimalLister al; Animal a1 = al.getNewAnimal(); Animal a2 = al.getNewAnimal(); }
Pros:
- Easy to understand.
- Requires no extra libraries or supporting code.
Cons:
- It requires
Animal
to have a well-behaved copy-constructor. - It can involve a lot of copying if
Animal
is larg and complex, although return value optimizationcan alleviate that in many situations. - Doesn't work if you plan on returning sub-classes derived from
Animal
as they will be sliceddown to a plainAnimal
, losing all the extra data in the sub-class.
Return a
shared_ptr<Animal>
:class AnimalLister { public: shared_ptr<Animal> getNewAnimal() { return new Animal(); } }; int main() { AnimalLister al; shared_ptr<Animal> a1 = al.getNewAnimal(); shared_ptr<Animal> a2 = al.getNewAnimal(); }
Pros:
- Works with object-hierarchies (no object slicing).
- No issues with having to copy large objects.
- No need for
Animal
to define a copy constructor.
Cons:
- Requires either Boost or TR1 libraries, or another smart-pointer implementation.
Track all
Animal
allocations inAnimalLister
class AnimalLister { vector<Animal *> Animals; public: Animal *getNewAnimal() { Animals.push_back(NULL); Animals.back() = new Animal(); return Animals.back(); } ~AnimalLister() { for(vector<Animal *>::iterator iAnimal = Animals.begin(); iAnimal != Animals.end(); ++iAnimal) delete *iAnimal; } }; int main() { AnimalLister al; Animal *a1 = al.getNewAnimal(); Animal *a2 = al.getNewAnimal(); } // All the animals get deleted when al goes out of scope.
Pros:
- Ideal for situations where you need a bunch of
Animal
s for a limited amount of time, and plan to release them all at once. - Easily adaptable to custom memory-pools and releasing all the
Animal
s in a singledelete
. - Works with object-hierarchies (no object slicing).
- No issues with having to copy large objects.
- No need for
Animal
to define a copy constructor. - No need for external libraries.
Cons:
- The implementation as written above is not thread-safe
- Requires extra support code
- Less clear than the previous two schemes
- It's non-obvious that when the AnimalLister goes out of scope, it's going to take the Animals with it. You can't hang on to the Animals any longer than you hang on the AnimalLister.
- Ideal for situations where you need a bunch of
每次创建动物时制作一个副本:
class AnimalLister { public: Animal getNewAnimal() { return Animal(); } }; int main() { AnimalLister al; Animal a1 = al.getNewAnimal(); Animal a2 = al.getNewAnimal(); }
优点:
- 容易理解。
- 不需要额外的库或支持代码。
缺点:
返回一个
shared_ptr<Animal>
:class AnimalLister { public: shared_ptr<Animal> getNewAnimal() { return new Animal(); } }; int main() { AnimalLister al; shared_ptr<Animal> a1 = al.getNewAnimal(); shared_ptr<Animal> a2 = al.getNewAnimal(); }
优点:
- 适用于对象层次结构(无对象切片)。
- 不必复制大对象没有问题。
- 无需
Animal
定义复制构造函数。
缺点:
- 需要 Boost 或 TR1 库,或其他智能指针实现。
跟踪所有
Animal
分配AnimalLister
class AnimalLister { vector<Animal *> Animals; public: Animal *getNewAnimal() { Animals.push_back(NULL); Animals.back() = new Animal(); return Animals.back(); } ~AnimalLister() { for(vector<Animal *>::iterator iAnimal = Animals.begin(); iAnimal != Animals.end(); ++iAnimal) delete *iAnimal; } }; int main() { AnimalLister al; Animal *a1 = al.getNewAnimal(); Animal *a2 = al.getNewAnimal(); } // All the animals get deleted when al goes out of scope.
优点:
- 非常适合在
Animal
有限的时间内需要大量s 并计划一次全部释放它们的情况。 - 轻松适应自定义内存池并
Animal
在单个delete
. - 适用于对象层次结构(无对象切片)。
- 不必复制大对象没有问题。
- 无需
Animal
定义复制构造函数。 - 不需要外部库。
缺点:
- 上面写的实现不是线程安全的
- 需要额外的支持代码
- 不如前两个方案清晰
- 不明显的是,当 AnimalLister 超出范围时,它会带走 Animals。你不能像挂在 AnimalLister 上一样挂在 Animals 上。
- 非常适合在
回答by Chris Jester-Young
I advise returning a std::tr1::shared_ptr
(or boost::shared_ptr
, if your C++ implementation does not have TR1) instead of a raw pointer. So, instead of using Animal*
, use std::tr1::shared_ptr<Animal>
instead.
我建议返回一个std::tr1::shared_ptr
(或者boost::shared_ptr
,如果你的 C++ 实现没有 TR1)而不是一个原始指针。因此,与其使用Animal*
,不如使用std::tr1::shared_ptr<Animal>
。
Shared pointers handle reference tracking for you, and delete the object automatically if there are no references left to it.
共享指针为您处理引用跟踪,并在没有引用的情况下自动删除对象。
回答by Igor Semenov
The simpliest way is to return smart pointer instead of regular pointers. For example:
最简单的方法是返回智能指针而不是常规指针。例如:
std::auto_ptr< Animal> getNewAnimal()
{
std::auto_ptr< Animal > animal1( new Animal() );
return animal1;
}
If you are able to use TR1 or Boost, you can also use shared_ptr<>.
如果您能够使用 TR1 或 Boost,您也可以使用 shared_ptr<>。
回答by itsmatt
Kind of a classic issue with pointers and allocated memory. It's about responsibility - who is responsible for cleaning up the memory allocated by the AnimalLister object.
指针和分配内存的经典问题。这是关于责任 - 谁负责清理 AnimalLister 对象分配的内存。
You could store off a pointer to each of those allocated Animals in the AnimalLister itself and have it clean things up.
您可以在 AnimalLister 本身中存储一个指向每个分配的 Animals 的指针,并让它清理一切。
But, you do have a couple of pointers to Animals sitting there in main() that would be referencing memory that was deleted.
但是,您确实有几个指向位于 main() 中的 Animals 的指针,它们将引用已删除的内存。
One of the reasons I think the reference counting solutions work better than rolling your own solution.
我认为引用计数解决方案比滚动您自己的解决方案更有效的原因之一。
回答by gbjbaanb
- shared_ptr (which works well),
- return a simple pointer and tell the user of your class that it is their animal now, and they have the responsibility to delete it when finished,
implement a 'freeAnimal(Animal*)' method that makes it obvious that deletion of the animal pointer is required.
An alternative way is to simply return the animal object directly, no pointers, no calls to new. The copy constructor will ensure the caller gets their own animal object that they can store on the heap or stack, or copy into a container as they desire.
- shared_ptr(效果很好),
- 返回一个简单的指针并告诉你的类的用户它现在是他们的动物,他们有责任在完成后删除它,
实现一个 'freeAnimal(Animal*)' 方法,该方法明显需要删除动物指针。
另一种方法是简单地直接返回动物对象,没有指针,也没有对 new 的调用。复制构造函数将确保调用者获得他们自己的动物对象,他们可以将其存储在堆或堆栈中,或者根据需要复制到容器中。
So:
所以:
class AnimalLister
{
Animal getAnimal() { Animal a; return a; }; // uses fast Return Value Optimisation
};
Animal myownanimal = AnimalLister.getAnimal(); // copy ctors into your Animal object
RVO means that returning the object instead of the pointer is actually faster (as the compiler doesn't create a new object and copies it into the caller's object, but uses the caller's object directly).
RVO 意味着返回对象而不是指针实际上更快(因为编译器不会创建新对象并将其复制到调用者的对象中,而是直接使用调用者的对象)。
回答by amit
In a thorough discussion by Scott Meyers, he concludes that using shared_ptr or auto_ptr is the best.
在Scott Meyers的深入讨论中,他得出结论,使用 shared_ptr 或 auto_ptr 是最好的。
回答by martinsb
The time to release the memory occupied by an object is when you don't need that particular object any more. In your particular case, the user of a class AnimalLister requested a pointer to a new allocated object of class Animal. So, he's the one that is responsible for freeing memory when he does need that pointer/object any more.
释放对象占用的内存的时间是当您不再需要该特定对象时。在您的特定情况下,AnimalLister 类的用户请求了一个指向 Animal 类的新分配对象的指针。因此,当他不再需要该指针/对象时,他负责释放内存。
AnimalLister lister;
Animal* a = lister.getNewAnimal();
a->sayMeow();
delete a;
In my opinion, there's no need to over-engineer anything in this case. AnimalLister is just a factory that creates new Animal objects and that's it.
在我看来,在这种情况下没有必要过度设计任何东西。AnimalLister 只是一个创建新 Animal 对象的工厂,仅此而已。
回答by brianb
Or you could follow the COM-ish approach, and apply simple reference counting.
或者您可以遵循 COM-ish 方法,并应用简单的引用计数。
- When you create the object, give it a reference value of 1 instantly
- When anyone gets a copy of the pointer, they AddRef()
- When anyone gives up their copy of the pointer, they Release()
- 创建对象时,立即给它一个参考值 1
- 当任何人获得指针的副本时,他们 AddRef()
- 当任何人放弃他们的指针副本时,他们 Release()
If the reference count hits 0, the object deletes itself.
如果引用计数达到 0,则对象将删除自身。
Its ultimately what the shared_ptr does under the hood, but it gives you more control over whats going on, and in my experience easier to debug. (Its also very cross-platform).
它最终是 shared_ptr 在幕后所做的,但它使您可以更好地控制正在发生的事情,并且根据我的经验更易于调试。(它也是非常跨平台的)。
I haven't given shared_ ptr too much of a chance in my development as yet, so that may serve your purposes perfectly.
在我的开发过程中,我还没有给 shared_ ptr 太多机会,因此它可以完美地满足您的目的。
回答by BigSandwich
I really like Josh's answer, but I thought I might throw in another pattern because it hasn't been listed yet. The idea is just force the client code to deal with keeping track of the animals.
我真的很喜欢 Josh 的回答,但我想我可能会采用另一种模式,因为它尚未列出。这个想法只是强制客户端代码处理跟踪动物。
class Animal
{
...
private:
//only let the lister create or delete animals.
Animal() { ... }
~Animal() { ... }
friend class AnimalLister;
...
}
class AnimalLister
{
static s_count = 0;
public:
~AnimalLister() { ASSERT(s_count == 0); } //warn if all animals didn't get cleaned up
Animal* NewAnimal()
{
++count;
return new Animal();
}
void FreeAnimal(Animal* a)
{
delete a;
--s_count;
}
}