智能指针
智能指针是封装了原始指针的智能对象,自动管理所指向的资源,避免内存泄漏和悬挂指针等问题,C++11引入了四种智能指针:unique_ptr、shared_ptr、weak_ptr、auto_ptr(C++17中已被弃用)。
1.unique_ptr
unique_ptr是一种独占式的智能指针,它拥有对象的唯一所有权,并且在对象生命周期结束时自动释放该对象。unique_ptr不能被复制和赋值,因此可以避免资源重复释放和悬挂指针等问题。unique_ptr适用于需要独占对象的情况,例如动态分配的内存和IO资源等。
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> ptr = std::make_unique<int>(5); //创建一个指向int类型的unique_ptr并初始化为5
std::cout << "ptr = " << *ptr << std::endl; //输出指针所指向的值
//动态分配一个数组,并将其绑定到unique_ptr
std::unique_ptr<int[]> arr_ptr(new int[3]);
arr_ptr[0] = 1;
arr_ptr[1] = 2;
arr_ptr[2] = 3;
std::cout << "arr_ptr[0] = " << arr_ptr[0] << std::endl; //输出数组第一个元素的值
//通过unique_ptr释放动态分配的内存
arr_ptr.reset(); //释放数组所占用的内存
return 0;
}
2.shared_ptr
shared_ptr是一种共享式的智能指针,它可以与多个指针共享对象,使用引用计数来跟踪对象的引用,并在所有引用都失效时自动释放对象。shared_ptr可以被复制和赋值,并且可以自动管理动态分配的内存。
#include <iostream>
#include <memory>
class Person {
public:
Person(const std::string& name) : name_(name) {}
~Person() {
std::cout << "Destroying " << name_ << std::endl;
}
void greet() const {
std::cout << "Hello, my name is " << name_ << std::endl;
}
private:
std::string name_;
};
int main() {
std::shared_ptr<Person> person1 = std::make_shared<Person>("Alice");
std::shared_ptr<Person> person2 = person1;
person1->greet();
person2->greet();
person1.reset();
if(person2) {
std::cout << "person2 is still alive!" << std::endl;
person2->greet();
}
return 0;
}
程序输出为:
Hello, my name is Alice
Hello, my name is Alice
Destroying Alice
person2 is still alive!
Hello, my name is Alice
Person
是一个拥有名称的类,shared_ptr
用于管理Person
对象的生命周期。在main
函数中,首先创建了一个Person
对象并将其分配给一个shared_ptr
(person1),然后又将相同的对象指针分配给另一个shared_ptr
(person2),因此同一对象现在由两个指针共享。
我们使用->
运算符来调用Person
类中的函数,并使用reset
函数来撤销person1指针。最后,我们检查person2指针是否仍然有效,并调用greet
函数,以确保Person对象仍然存在。
3.weak_ptr
weak_ptr是一种弱引用智能指针,它是一种指向某个对象的指针,但它不会增加对象的引用计数,也不会拥有对象的所有权。相反,它只是提供了一种访问被其他shared_ptr
所控制的对象的方法。如果被其它shared_ptr
所控制的对象已经被销毁,那么weak_ptr
会自动置为空,避免了悬空指针的问题。
weak_ptr
一般在以下几种场景下使用:
- 在使用
shared_ptr
的循环引用中,一般使用weak_ptr
打破循环引用,避免造成内存泄漏。 - 在缓存中使用
weak_ptr
,可以避免因缓存中的对象被占用而导致的内存过多占用。 - 在异步操作中使用
weak_ptr
,可以避免异步操作完成后访问到已经被销毁的对象而导致程序崩溃。
可以通过lock()
函数获取weak_ptr
指向的对象的shared_ptr
,从而访问被其它shared_ptr
控制的对象,或者判断对象是否已经被销毁。如果weak_ptr
已经失效或者指向的对象已经被销毁,lock()
函数会返回空的shared_ptr
。
#include <iostream>
#include <memory>
class MyClass {
public:
MyClass(int value) : m_value(value) {
std::cout << "Constructing MyClass object with value " << m_value << std::endl;
}
void doSomething() {
std::cout << "MyClass object with value " << m_value << " is doing something!" << std::endl;
}
private:
int m_value;
};
int main() {
// Create a shared pointer to a MyClass object with value 42
std::shared_ptr<MyClass> myClassSharedPtr = std::make_shared<MyClass>(42);
// Create a weak pointer to the same MyClass object
std::weak_ptr<MyClass> myClassWeakPtr = myClassSharedPtr;
// Use the shared pointer to call the doSomething() method
myClassSharedPtr->doSomething();
// Check if the weak pointer still points to the MyClass object
if (std::shared_ptr<MyClass> myClassSharedPtr2 = myClassWeakPtr.lock()) {
std::cout << "Weak pointer is still valid!" << std::endl;
// Use the shared pointer obtained from the weak pointer to call the doSomething() method
myClassSharedPtr2->doSomething();
} else {
std::cout << "Weak pointer is no longer valid!" << std::endl;
}
return 0;
}
在这个代码中,我们创建了一个shared_ptr指向MyClass对象,然后又创建了一个weak_ptr指向同一个MyClass对象。然后我们使用shared_ptr调用了MyClass对象的doSomething()方法。接着,我们通过lock()方法检查了weak_ptr是否仍然指向同一个MyClass对象,如果是,我们使用lock()方法返回的shared_ptr再次调用了doSomething()方法。如果不是,则说明MyClass对象已经被销毁了,我们就不能再使用它了。