简介
智能指针是一种特殊的指针类型,它能够自动管理内存资源,避免常见的内存泄漏和多次释放等。在C++ 11标准中出现了新的智能指针unique_ptr、 shared_ptr、weak_ptr等。
std::unique_ptr
用于管理动态分配的内存资源,它提供了自动释放内存的功能。与原始指针相比,unique_ptr有更高的安全性和易用性。
特点: 所有权唯一、自动释放内存、指针语义、禁止拷贝和权限转移、自定义删除器
所有权唯一:每个unique_ptr实例拥有对其所指向对象的唯一所有权。这意味着在任何时候只有一个unique_ptr可以指向一个特定的对象。
自动释放内存:当unique_ptr超出作用域或被重新赋值时,它所管理的内存会自动释放。这样就避免了内存泄漏的问题。
指针语义:使用方式与原始指针相似,可通过指针操作符(->)和解引用操作符(*)来访问所指向对象成员。
禁止拷贝:unique_ptr禁止拷贝,即不能进行复制构造和赋值操作。这是为了确保独占所有权的特性,防止多个指针同时管理同一个对象的内存。
权限转移:支持移动构造和移动赋值操作,将所有权转移给新的unique_ptr而旧指针则自动释放置空,无需进行内存拷贝。
自定义删除器:unique_ptr可通过模板参数来指定一个删除器(deleter)函数对象,用于在释放内存时执行额外的清理操作。
缺点: 使用裸指针赋值或捕获会很容易非常的乱、导致内存泄露,尽量避免智能指针和普通指针的混合。
常见成员函数:
常见成员函数:
operator* 解引用操作符,用于获取 unique_ptr 所指向对象的引用。
operator-> 箭头操作符,用于通过 unique_ptr 访问对象的成员函数或成员变量。
get 返回指向所管理对象的裸指针。
reset 重置 unique_ptr,释放当前所管理的对象并接管新的对象。
release 释放对所管理对象的控制权,并返回该指针的裸指针。
swap 交换两个 unique_ptr 的内容。
示例测试:
基本操作1
#include <iostream>
void unique_operate1()
{
/******************** 创建 ********************/
// 创建方式一,初始化后修改值
std::unique_ptr<int> pInt1(new int(10));
*pInt1 = 101;
// 创建方式二,初始化后修改值
std::unique_ptr<int> pInt2 = std::unique_ptr<int>(new int(20));
*pInt2 = 102;
// 创建方式三,初始化后修改值
std::unique_ptr<int> pInt3 = std::make_unique<int>(30);
*pInt3 = 103;
// 创建多个int指针对象,初始化后修改值
std::unique_ptr<int[]> pIntArray(new int[5]{ 10,20,30,40,50 });
pIntArray[0] = 1;
pIntArray[1] = 2;
pIntArray[2] = 3;
pIntArray[3] = 4;
pIntArray[4] = 5;
/*********************************************/
/******************** 输出地址及值 ********************/
std::cout << pInt1 << " " << *pInt1 << " | " << pInt1.get() << " " << *(pInt1.get()) << std::endl;
std::cout << pInt1 << " " << *pInt1 << " | " << pInt1.get() << " " << *(pInt1.get()) << std::endl;
std::cout << pInt1 << " " << *pInt1 << " | " << pInt1.get() << " " << *(pInt1.get()) << std::endl;
/*
输出为:
000002110F242540 101 | 000002110F242540 101
000002110F242540 101 | 000002110F242540 101
000002110F242540 101 | 000002110F242540 101
*/
/*********************************************/
/******************** 权限转移 ********************/
std::cout << pInt1 << " " << *pInt1 << std::endl;
//std::unique_ptr<int> P1 = pInt1; // 错误操作
std::unique_ptr<int> P1 = std::move(pInt1); // 正确操作。此时P1接收了pInt1的地址和值,而pInt1则被置空
std::cout << P1 << " " << *P1 << std::endl;
/*
注意:
pInt1由于被置空,此时无法使用否则出现异常。
输出为:
000002303C431670 101
000002303C431670 101
*/
/*********************************************/
}
int main()
{
// 基本操作1
unique_operate1();
return 0;
}
基本操作2
#include <iostream>
class Operate2
{
public:
Operate2()
{
std::cout << "call Operate2() \t" << this << std::endl;
}
Operate2(const Operate2 &)
{
std::cout << "call Operate2(const Operate2 &) \t" << this << std::endl;
}
~Operate2()
{
std::cout << "call ~Operate2() \t" << this << std::endl;
}
Operate2* operator +(int v)
{
std::cout << "call operator + \t" << this << std::endl;
Operate2 *tmp = new Operate2;
tmp->value = value + v;
return tmp;
}
// 设置value值
void setValue(const int &v){ value = v; }
// 获取value值
int getValue()const { return value; }
private:
int value = 10;
};
void unique_operate2()
{
// 进入一次Operate2构造(ptr1)
std::unique_ptr<Operate2> ptr1 = std::make_unique<Operate2>();
/******************** 成员操作 ********************/
// 测试
std::cout << "print ptr1 ptr add:" << ptr1 << std::endl << std::endl;
// 测试 ->
std::cout << "print ptr2 - value is:" << ptr1->getValue() << std::endl;
ptr1->setValue(20);
std::cout << "print ptr2 - value is:" << ptr1->getValue() << std::endl << std::endl;
// 测试 get :返回指向所管理对象的裸指针
Operate2 *tOperate2Ptr = ptr1.get();
std::cout << "tOperate2Ptr - value is:" << ptr1->getValue() << std::endl << std::endl;
/*
测试 reset :该方法可让unique_ptr解除对原始内存管理,也可初始化一个独占的智能指针
reset() 解除对原始内存的管理
reset(new xxx()) 重新指定智能指针管理的原始内存
*/
ptr1.reset(new Operate2()); // 新创建的进入一次构造,然后之前的对象进入一次析构
std::cout << "new print ptr2 - value is:" << ptr1->getValue() << std::endl << std::endl;
// 测试 release :释放对所管理对象的控制权,并返回该指针的裸指针
Operate2 *ttOperate2Ptr = ptr1.release(); // ptr1被置空
if (ttOperate2Ptr) { delete ttOperate2Ptr; }ttOperate2Ptr = nullptr;
std::cout << std::endl;
// 测试 swap :交换两个unique_ptr的内容
std::unique_ptr<Operate2> Operate2_1 = std::make_unique<Operate2>(); Operate2_1->setValue(6);
std::unique_ptr<Operate2> Operate2_2 = std::make_unique<Operate2>(); Operate2_2->setValue(9);
std::cout << "Operate2_1:" << Operate2_1->getValue() << " Operate2_2:" << Operate2_1->getValue() << std::endl;
Operate2_1.swap(Operate2_2);
std::cout << "Operate2_1:" << Operate2_1->getValue() << " Operate2_2:" << Operate2_1->getValue() << std::endl;
/*********************************************/
// 函数结束,进入一次Operate2析构(ptr1)
}
int main()
{
// 基本操作2
unique_operate2();
/*
输出为:
call Operate2() 0000029218F50140
print ptr1 ptr add:0000029218F50140
print ptr2 - value is:10
print ptr2 - value is:20
tOperate2Ptr - value is:20
call Operate2() 0000029218F501C0
call ~Operate2() 0000029218F50140
new print ptr2 - value is:10
call ~Operate2() 0000029218F501C0
call Operate2() 0000029218F50140
call Operate2() 0000029218F501C0
Operate2_1:6 Operate2_2:6
Operate2_1:9 Operate2_2:9
call ~Operate2() 0000029218F50140
call ~Operate2() 0000029218F501C0
*/
return 0;
}
基本操作3
#include <iostream>
class TestClass
{
public:
TestClass()
{
std::cout << "call TestClass() \t" << this << std::endl;
}
TestClass(const TestClass &)
{
std::cout << "call TestClass(const TestClass &) \t" << this << std::endl;
}
~TestClass()
{
std::cout << "call ~TestClass() \t" << this << std::endl;
}
TestClass* operator +(int v)
{
std::cout << "call TestClass + \t" << this << std::endl;
TestClass *tmp = new TestClass;
tmp->value = value + v;
return tmp;
}
// 设置value值
void setValue(const int &v){ value = v; }
// 获取value值
int getValue()const { return value; }
private:
int value = 100;
};
std::unique_ptr<TestClass> changeValue_add10(std::unique_ptr<TestClass> tcPtr)
{
tcPtr->setValue(tcPtr->getValue() + 10);
return tcPtr;
}
void unique_operate3()
{
std::unique_ptr<TestClass> tcPtr1 = std::make_unique<TestClass>();
if (tcPtr1) { std::cout << "11111:\t" << "ptr:" << tcPtr1 << " *ptr:" << tcPtr1->getValue() << std::endl; }
//changeValue_add10(stcPtr1); // 错误:由于unique_ptr不能拷贝,故不得作为形参进行拷贝给实参
std::unique_ptr<TestClass> tcPtr2 = changeValue_add10(std::move(tcPtr1));
if (tcPtr2) { std::cout << "22222:\t" << "ptr:" << tcPtr2 << " *ptr:" << tcPtr2->getValue() << std::endl; }
}
int main()
{
// 基本操作3
unique_operate3();
/*
输出为:
call TestClass() 000001F354580A60
11111: ptr:000001F354580A60 *ptr:100
22222: ptr:000001F354580A60 *ptr:110
call ~TestClass() 000001F354580A60
*/
return 0;
}
关注
笔者 - jxd