简介
本文介绍C++11中的智能指针的基本用法和运用场景
三种指针概述
在C++中,有三种智能指针,分别是:unique_ptr、shared_ptr和weak_ptr。这三种智能指针都是在STL的头文件< memory> 中定义的,可以在C++11及其以上版本中使用。
unique_ptr
unique_ptr是一种独占型智能指针,它的主要作用是管理动态分配的对象。unique_ptr类的特点是:只能有一个unique_ptr指向同一个对象,当此unique_ptr离开作用域时,其所指向的对象也会被自动释放。因为只有一个指针可以指向对象,所以可以保证指针不会被误用,进而避免内存泄漏等问题。
使用方法:
std::unique_ptr<Type> ptr(new Type(args...)); // 使用new表达式创建一个动态分配的对象,并将其托管给unique_ptr
示例代码如下:
#include <iostream>
#include <memory>
class Test {
public:
Test() { std::cout << "Test Constructor" << std::endl; }
~Test() { std::cout << "Test Destructor" << std::endl; }
};
int main() {
std::unique_ptr<Test> ptr(new Test());
// 使用智能指针操作对象
std::cout << "Unique pointer is " << (ptr ? "not null" : "null") << std::endl;
return 0;
}
在上述代码中,我们定义了一个名为Test的类,并在main函数中使用unique_ptr动态分配了一个对象。可以看到,当程序执行到离开作用域时,会自动调用Test对象的析构函数,输出如下:
Test Constructor
Unique pointer is not null
Test Destructor
shared_ptr
shared_ptr是一种共享型智能指针,它的主要作用是管理动态分配的对象,并与其他shared_ptr共享这个对象的所有权。每个shared_ptr内部维护一个引用计数器,记录当前共享此对象的指针数目。当引用计数器变为0时,该对象将被自动释放。
使用方法:
std::shared_ptr<Type> ptr1 = std::make_shared<Type>(args...); // 使用make_shared函数创建一个动态分配的对象,并将其托管给shared_ptr
std::shared_ptr<Type> ptr2(ptr1); // 创建一个新的shared_ptr,与ptr1共享所有权
示例代码如下:
#include <iostream>
#include <memory>
class Test {
public:
Test(int value) : mValue(value) { std::cout << "Test Constructor with value: " << value << std::endl; }
~Test() { std::cout << "Test Destructor with value: " << mValue << std::endl; }
void SetValue(int value) { mValue = value; }
int GetValue() const { return mValue; }
private:
int mValue;
};
void PrintSharedPtrInfo(const std::shared_ptr<Test>& ptr) {
std::cout << "Shared pointer value is: " << ptr->GetValue() << ", use count is: " << ptr.use_count() << std::endl;
}
int main() {
std::shared_ptr<Test> ptr1 = std::make_shared<Test>(10);
std::shared_ptr<Test> ptr2 = ptr1; // ptr2和ptr1共享所有权
PrintSharedPtrInfo(ptr1);
PrintSharedPtrInfo(ptr2);
ptr1->SetValue(100);
PrintSharedPtrInfo(ptr1);
PrintSharedPtrInfo(ptr2);
return 0;
}
在上述代码中,我们定义了一个名为Test的类,并在main函数中使用shared_ptr动态分配了一个对象,并使用两个shared_ptr对象共享其所有权。通过输出它们的值和引用计数,我们可以看到它们都共享同一个对象。同时,我们也可以修改这个对象的值,然后再次输出,可以发现修改后的值对两个shared_ptr对象都是可见的。输出如下:
Test Constructor with value: 10
Shared pointer value is: 10, use count is: 2
Shared pointer value is: 10, use count is: 2
Shared pointer value is: 100, use count is: 2
Shared pointer value is: 100, use count is: 2
Test Destructor with value: 100
weak_ptr
weak_ptr是一种弱引用智能指针,其主要作用是解决shared_ptr的循环引用问题。由于shared_ptr是共享型智能指针,当两个shared_ptr对象相互引用时,容易形成循环引用,导致内存泄漏等问题。weak_ptr可以创建一个对shared_ptr对象的非拥有性引用,并且不会导致引用计数加1,这样可以避免循环引用问题。
使用方法:
std::weak_ptr<Type> ptr; // 创建一个空的weak_ptr对象
std::shared_ptr<Type> sharedPtr = std::make_shared<Type>(args...);
ptr = sharedPtr; // 将shared_ptr赋值给weak_ptr
std::shared_ptr<Type> sharedPtr2 = ptr.lock(); // 使用lock函数获取可用的shared_ptr对象
ps:需要注意的是,weak_ptr所指向的对象可能已被其他原因销毁,此时lock函数返回一个空的shared_ptr对象。在使用shared_ptr之前,应该先检查它是否为空
示例代码如下:
#include <iostream>
#include <memory>
class Test;
void PrintWeakPtrInfo(const std::weak_ptr<Test>& ptr) {
std::cout << "Weak pointer is " << (ptr.expired() ? "expired" : "not expired") << std::endl;
}
class Test {
public:
Test(int value) : mValue(value) { std::cout << "Test Constructor with value: " << value << std::endl; }
~Test() { std::cout << "Test Destructor with value: " << mValue << std::endl; }
void SetNext(std::weak_ptr<Test> next) {
mNext = next;
}
std::weak_ptr<Test> GetNext() const {
return mNext;
}
private:
int mValue;
std::weak_ptr<Test> mNext;
};
int main() {
std::shared_ptr<Test> ptr1 = std::make_shared<Test>(10);
std::shared_ptr<Test> ptr2 = std::make_shared<Test>(20);
// 使用weak_ptr解决shared_ptr循环引用问题
ptr1->SetNext(ptr2);
ptr2->SetNext(ptr1);
PrintWeakPtrInfo(ptr1->GetNext());
PrintWeakPtrInfo(ptr2->GetNext());
// 获取对象的shared_ptr,如果对象已经被销毁则返回null
std::shared_ptr<Test> sharePtr1 = ptr1->GetNext().lock();
std::shared_ptr<Test> sharePtr2 = ptr2->GetNext().lock();
if (sharePtr1) {
std::cout << "Share pointer value is: " << sharePtr1->GetValue() << std::endl;
} else {
std::cout << "Share pointer is not available" << std::endl;
}
if (sharePtr2) {
std::cout << "Share pointer value is: " << sharePtr2->GetValue() << std::endl;
} else {
std::cout << "Share pointer is not available" << std::endl;
}
return 0;
}
在上述代码中,我们定义了一个名为Test的类,并在main函数中使用两个shared_ptr动态分配了两个对象。然后,我们通过调用SetNext函数建立了两个对象之间的循环引用关系。接着,我们使用weak_ptr对象打破循环引用,获取这两个对象的下一个指针,并输出弱引用指针的状态。最后,我们使用lock函数获取可用的shared_ptr对象,并输出其值。由于测试程序没有显式释放内存,因此在程序结束时自动调用析构函数删除对象。输出如下:
Test Constructor with value: 10
Test Constructor with value: 20
Weak pointer is not expired
Weak pointer is not expired
Share pointer value is: 20
Share pointer value is: 10
Test Destructor with value: 10
Test Destructor with value: 20
三种指针的适用场景
1、unique_ptr是一种独占式智能指针,它管理着一个动态分配的对象,并确保在离开作用域时销毁该对象。它不能拷贝,只能移动,因此每个unique_ptr对象都是独一无二的。unique_ptr适用于需要在一个作用域内拥有一个对象的情况,例如函数中分配的临时对象或者容器中的元素。
2、shared_ptr是一种共享式智能指针,它也管理着一个动态分配的对象,并可以被多个shared_ptr对象共享所有权。它采用引用计数的方式来管理对象的生命周期,在所有shared_ptr对象都失效后才会销毁对象。shared_ptr适用于需要多个对象共享同一份数据的情况,例如多线程环境中的数据共享和实现观察者模式等。
3、weak_ptr是一种弱引用智能指针,它不对对象的生命周期产生影响,只是通过与shared_ptr对象配合使用,提供一种解决shared_ptr循环引用问题的方法。weak_ptr可以从一个shared_ptr或另一个weak_ptr对象构造而来,并且可以判断其指向的对象是否还存在,但是不能直接访问其指向的对象,需要调用lock()函数获取一个可用的shared_ptr对象。
总结
总之,选择何种智能指针取决于具体场景。如果需要拥有对象的独占权,那么应该选择unique_ptr;如果需要共享对象所有权,那么应该选择shared_ptr;如果需要打破shared_ptr循环引用,那么应该选择weak_ptr。