在一个参数中释放已分配的内存
在现代C++(C++11及以后版本)中,这里有一个简单的答案:
- "什么是智能指针?"
它是一种像指针一样使用的类型,但是它提供了自动内存管理的附加功能:当智能指针不再被使用时,它指向的内存会被释放(另见维基百科上更详细的定义)。 - "什么时候应该使用智能指针?"
在涉及跟踪一段内存的所有权、分配或释放内存的代码中,智能指针通常可以帮助你省去显式操作。 - "但我应该在哪种情况下使用哪种智能指针?"
- 当你想让对象的生存期仅限于单一拥有引用它的生存期时,使用
std::unique_ptr
。例如,用它来指向在进入某个作用域时分配的内存并在退出该作用域时释放的指针。 - 当你想从多个地方引用对象,但不希望在这些引用本身消失之前将对象释放时,请使用
std::shared_ptr
。 - 当你想从多个地方引用对象,并且可以忽略并释放其中某些引用时,请使用
std::weak_ptr
(因此当你尝试解引用时,它将只表示对象已不存在)。 - 提议在C++26中添加hazard指针,但现在还没有。
- 除非特殊情况必须使用
boost::
智能指针或std::auto_ptr
,否则不要使用它们。
- 当你想让对象的生存期仅限于单一拥有引用它的生存期时,使用
- "嘿,我没问你应该用哪一个!"
啊,但你真的想问,不要否认。 - "那么我什么时候应该使用常规指针?"
大多数情况下,在对内存所有权没有意识的代码中使用。这通常是在从某个地方获取指针的函数中,不分配也不释放内存,并且不存储比其执行时间更长久的指针的副本。
更新
这条回答有些陈旧了,因此描述的是当时“好”的东西,即Boost库提供的智能指针。自C++11以来,标准库已经提供了足够的智能指针类型,因此您应该更倾向于使用 std::unique_ptr
、std::shared_ptr
和 std::weak_ptr
。
还有一个叫做std::auto_ptr
的东西。它非常像一个作用域指针,除了它还有一种“特殊”的危险能力,可以被复制,这也意外地转移了所有权。
C++11中已被弃用,C++17中已被删除,因此您不应该使用它。
std::auto_ptrp1 (new MyObject()); std::auto_ptr p2 = p1; // Copy and transfer ownership. // p1 gets set to empty! p2->DoSomething(); // Works. p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.
旧版回答
智能指针是一种将“原始”(或“裸”)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...?
相比之下,智能指针定义了一个对象何时被销毁的策略。你仍然必须创建对象,但你再也不用担心销毁它了。
SomeSmartPtrptr(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() { { std::unique_ptrptr(new MyObject()); ptr->DoSomethingUseful(); } // ptr goes out of scope -- // the MyObject is automatically destroyed. // ptr->Oops(); // Compile error: "ptr" not defined // since it is no longer in scope. }
请注意,std::unique_ptr
实例无法复制。这可以防止指针多次被错误地删除。但是,您可以将引用传递给其他调用的函数。
std::unique_ptr
在您希望将对象的生命周期绑定到特定代码块或者作为另一个对象的成员数据嵌入其中的情况下非常有用,该对象将一直存在,直到包含该代码块的作用域退出,或者直到包含的对象本身被销毁。
更复杂的智能指针策略涉及对指针进行引用计数。这允许指针被复制。当最后一个指向对象的“引用”被销毁时,该对象会被删除。此策略由 boost::shared_ptr
和std::shared_ptr
实现。
void f() { typedef std::shared_ptrMyObjectPtr; // 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 { std::shared_ptrother; }; std::shared_ptr p1 (new Owner()); std::shared_ptr 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
。