什么是智能指针,何时应使用?
什么是智能指针,何时应使用?
解决方案
http://en.wikipedia.org/wiki/Smart_pointer
In computer science, a smart pointer is an abstract data type that simulates a pointer while providing additional features, such as automatic garbage collection or bounds checking. These additional features are intended to reduce bugs caused by the misuse of pointers while retaining efficiency. Smart pointers typically keep track of the objects that point to them for the purpose of memory management. The misuse of pointers is a major source of bugs: the constant allocation, deallocation and referencing that must be performed by a program written using pointers makes it very likely that some memory leaks will occur. Smart pointers try to prevent memory leaks by making the resource deallocation automatic: when the pointer to an object (or the last in a series of pointers) is destroyed, for example because it goes out of scope, the pointed object is destroyed too.
智能指针就像常规的(类型化的)指针一样,如" char ",除了当指针本身超出范围时,它所指向的内容也将被删除。我们可以像使用常规指针一样使用"->"来使用它,但是如果我们需要实际的数据指针,则不需要使用它。为此,我们可以使用"&ptr"。
它对以下用途很有用:
- 必须使用new分配的对象,但是我们希望拥有与该堆栈上的对象相同的生存期。如果将对象分配给智能指针,则当程序退出该功能/块时,它们将被删除。
- 类的数据成员,以便在删除对象时也删除所有拥有的数据,而在析构函数中没有任何特殊代码(我们将需要确保析构函数是虚拟的,这几乎总是一件好事) 。
在以下情况下,我们可能不想使用智能指针:
- ...指针实际上不应该拥有数据...,即,当我们仅使用数据时,但是我们希望它在引用它的函数中保留下来。
- ...智能指针本身不会在某个时候被破坏。我们不希望它位于永不被破坏的内存中(例如,在动态分配但不会被明确删除的对象中)。
- ...两个智能指针可能指向相同的数据。 (但是,还有更聪明的指针可以处理此问题……这称为引用计数。)
也可以看看:
- 垃圾收集。
- 有关数据所有权的此堆栈溢出问题
大多数类型的智能指针都可以为我们处理指针指向的对象。这非常方便,因为我们不必再考虑手动处理对象。
最常用的智能指针是" std :: tr1 :: shared_ptr"(或者" boost :: shared_ptr")和(不太常见的)std :: auto_ptr。我建议定期使用shared_ptr
。
shared_ptr
是非常通用的,并且处理各种各样的处理方案,包括需要"跨越DLL边界传递"对象的情况(如果在代码和DLL之间使用不同的libc
,这是常见的噩梦)。
智能指针是类似指针的类型,具有一些其他功能,例如自动内存重新分配,引用计数等
"智能指针什么,为什么,哪个?"页上提供了小型介绍。
一种简单的智能指针类型是" std :: auto_ptr"(C ++标准的20.4.5章),它允许在超出范围时自动释放内存,并且比抛出异常时的简单指针使用更健壮,虽然不太灵活。
另一个方便的类型是boost :: shared_ptr
,它实现引用计数并在没有对象引用时自动分配内存。这有助于避免内存泄漏,并且易于使用来实现RAII。
戴维·范德沃德(David Vandevoorde),尼古拉·乔苏蒂斯(Nicolai M. Josuttis)第20章"智能指针"一书中详细介绍了该主题。
涵盖了一些主题:
- 防止异常
- 持有人,(请注意,std :: auto_ptr是此类智能指针的实现)
- 资源获取即初始化(在C ++中经常用于异常安全的资源管理)
- 持有人限制
- 参考计数
- 并发计数器访问
- 销毁和重新分配
智能指针是包装"原始"(或者"裸露")C ++指针的类,用于管理所指向对象的生命周期。没有单一的智能指针类型,但是它们都尝试以一种实用的方式抽象一个原始指针。
智能指针应该比原始指针更可取。如果我们觉得需要使用指针(首先考虑是否确实需要使用指针),通常会希望使用智能指针,因为这可以缓解原始指针的许多问题,主要是忘记删除对象和泄漏内存。
使用原始指针,程序员必须在不再有用时显式销毁该对象。
// Need to create the object to achieve some goal MyObject* ptr = new MyObject(); ptr->DoSomething(); // Use the object in some way delete ptr; // Destroy the object. Done with it. // Wait, what if DoSomething() raises an exception...?
通过比较,智能指针定义了有关销毁对象的时间的策略。我们仍然必须创建对象,但是不必担心销毁它。
SomeSmartPtr<MyObject> ptr(new MyObject()); ptr->DoSomething(); // Use the object in some way. // Destruction of the object happens, depending // on the policy the smart pointer class uses. // Destruction would happen even if DoSomething() // raises an exception
使用中最简单的策略涉及智能指针包装器对象的范围,例如,由boost :: scoped_ptr
或者std :: unique_ptr
实现。
void f() { { boost::scoped_ptr<MyObject> ptr(new MyObject()); ptr->DoSomethingUseful(); } // boost::scopted_ptr goes out of scope -- // the MyObject is automatically destroyed. // ptr->Oops(); // Compile error: "ptr" not defined // since it is no longer in scope. }
请注意,不能复制scoped_ptr
实例。这样可以防止多次(错误地)删除指针。但是,我们可以将对其的引用传递给我们调用的其他函数。
当我们要将对象的生存期绑定到特定代码块时,或者如果将其作为成员数据嵌入另一个对象的生存期中,则作用域指针非常有用。该对象将一直存在,直到退出包含代码块,或者直到包含对象本身被销毁为止。
更复杂的智能指针策略涉及对指针进行引用计数。这确实允许复制指针。当对对象的最后一个"引用"被销毁时,该对象将被删除。该策略由boost :: shared_ptr
和std :: shared_ptr
实施。
void f() { typedef std::shared_ptr<MyObject> MyObjectPtr; // nice short alias MyObjectPtr p1; // Empty { MyObjectPtr p2(new MyObject()); // There is now one "reference" to the created object p1 = p2; // Copy the pointer. // There are now two references to the object. } // p2 is destroyed, leaving one reference to the object. } // p1 is destroyed, leaving a reference count of zero. // The object is deleted.
当对象的生存期更加复杂,并且不直接与代码的特定部分或者另一个对象绑定时,引用计数的指针非常有用。
引用计数指针的一个缺点是可能会创建一个悬空的引用:
// Create the smart pointer on the heap MyObjectPtr* pp = new MyObjectPtr(new MyObject()) // Hmm, we forgot to destroy the smart pointer, // because of that, the object is never destroyed!
另一种可能性是创建循环引用:
struct Owner { boost::shared_ptr<Owner> other; }; boost::shared_ptr<Owner> p1 (new Owner()); boost::shared_ptr<Owner> p2 (new Owner()); p1->other = p2; // p1 references p2 p2->other = p1; // p2 references p1 // Oops, the reference count of of p1 and p2 never goes to zero! // The objects are never destroyed!
为了解决这个问题,Boost和C ++ 11都定义了一个" weak_ptr"来定义对" shared_ptr"的弱引用(未计数)。
更新
这个答案很旧,因此描述了当时什么是"好",这是Boost库提供的智能指针。从C ++ 11开始,标准库提供了足够的智能指针类型,因此我们应该赞成使用std :: unique_ptr
,std :: shared_ptr
和std :: weak_ptr
。
还有std :: auto_ptr
。它非常类似于作用域指针,不同之处在于它还具有"特殊"危险功能,可被复制,这也意外地转移了所有权!最新标准不推荐使用它,因此我们不应该使用它。使用std :: unique_ptr
代替。
std::auto_ptr<MyObject> p1 (new MyObject()); std::auto_ptr<MyObject> p2 = p1; // Copy and transfer ownership. // p1 gets set to empty! p2->DoSomething(); // Works. p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.
Chris,Sergdev和Llyod提供的定义是正确的。不过,我更喜欢一个简单的定义,只是为了让我的生活更简单:
智能指针只是一个重载->和
*`运算符的类。这意味着对象在语义上看起来像一个指针,但是我们可以使它做一些更酷的事情,包括引用计数,自动销毁等。
在大多数情况下," shared_ptr"和" auto_ptr"就足够了,但是它们也带有它们自己的一组小特质。