文章目录
C++智能指针之unique_ptr
unique_ptr篇
简介
unique_ptr是一种定义在 <memory>
中的智能指针,管理指针的存储,提供智能资源回收,并且与内置指针相比几乎没有开销(取决于使用的删除器)。它持有对对象的独有权,即两个unique_ptr不能指向一个对象,不能进行复制操作只能进行移动操作,离开作用域后自动删除指向的对象,或者显示调用 unique_ptr::reset。
成员函数
构造函数
constexpr unique_ptr() noexcept; // 默认构造函数1
std::unique_ptr<int> u1;
constexpr unique_ptr (nullptr_t) noexcept : unique_ptr() {} // 默认构造函数2:传入空指针
std::unique_ptr<int> u2 (nullptr);
explicit unique_ptr (pointer p) noexcept; // 传入其它指针
std::unique_ptr<int> u3 (new int);
unique_ptr (pointer p,
typename conditional<is_reference<D>::value,D,const D&> del) noexcept; // 传入指针和左值删除器
std::default_delete<int> d;
std::unique_ptr<int> u4 (new int, d);
unique_ptr (pointer p,typename remove_reference<D>::type&& del) noexcept; // 传入指针和右值删除器
std::unique_ptr<int> u5 (new int, std::default_delete<int>());
unique_ptr (unique_ptr&& x) noexcept; // 调用move构造
std::unique_ptr<int> u6 (std::move(u5));
template <class U, class E>
unique_ptr (unique_ptr<U,E>&& x) noexcept; // 调用move右值构造
std::unique_ptr<int> u7 (std::move(u6));// u6->nullptr u7-> u6
template <class U>
unique_ptr (auto_ptr<U>&& x) noexcept; // 通过移动 auto_ptr 构造, 注意:c++11 已经不支持auto_ptr(c++98)
std::unique_ptr<int> u8 (std::auto_ptr<int>(new int))
unique_ptr (const unique_ptr&) = delete; // 拷贝构造
int *p = new int;
*p = 10;
std::unique_ptr<int> u9 (p);
析构函数
如果对象是空(nullptr)则析构无效:
// ~unique_ptr();
// unique_ptr destructor example
#include <iostream>
#include <memory>
int main () {
auto deleter = [](int*p){
delete p;
std::cout << "[deleter called]\n";
};
std::unique_ptr<int,decltype(deleter)> foo (new int,deleter);
std::cout << "foo " << (foo?"is not":"is") << " empty\n";
return 0; // [deleter called]
}
get
返回存储的指针。
pointer get() const noexcept;
#include <iostream>
#include <memory>
int main()
{
std::unique_ptr<int> foo(new int);
int *p = nullptr;
p = foo.get();
*p = 10;
std::cout << foo.get() << ":" << *foo << "\n";
std::cout << p << ":" << *p << "\n";
return 0;
}
/*
output:
0000023D045A6010:10
0000023D045A6010:10
*/
get_deleter
返回对象存储的删除器。
deleter_type& get_deleter() noexcept;
const deleter_type& get_deleter() const noexcept;
#include <iostream>
#include <memory>
class myDeleter {
int m_count;
public:
myDeleter() :m_count(0){}
template <typename T>
void operator()(T* p) { // 重载操作符()
if (p)
{
delete p;
p = nullptr;
std::cout << "deleter #" << ++m_count << '\n';
}
}
};
int main()
{
{
myDeleter del;
std::unique_ptr<int, myDeleter> one(new int, del); // 值
std::unique_ptr<int, myDeleter> two(new int, one.get_deleter()); // 值
std::unique_ptr<int, myDeleter&> three(new int, one.get_deleter()); // 引用
std::unique_ptr<int, myDeleter&> four(new int, del); // 值
*one = 69;
std::cout << one.get() << ":" << *one << "\n";
one.reset(); // count = 1
*two = 96;
std::cout << two.get() << ":" << *two << "\n";
two.reset(); // count = 1
*three = 36;
std::cout << three.get() << ":" << *three << "\n";
three.reset(); // count = 2
*four = 63;
std::cout << four.get() << ":" << *four << "\n";
four.reset(); // count = 1
}
std::cin.get();
return 0;
}
/*
output:
00000208D9216010:69
deleter #1
00000208D9216050:96
deleter #1
00000208D9216090:36
deleter #2
00000208D9215AB0:63
deleter #1
*/
release
释放对象存储的指针和对象所有权,并返回该指针。
pointer release() noexcept;
#include <iostream>
#include <memory>
int main()
{
std::unique_ptr<int> foo(new int);
int* p = foo.release();
*p = 10;
std::cout << foo.get() << "\n";
std::cout << p << ":" << *p << "\n";
delete p; // 注意:foo对象释放所有权后不再自动释放.
return 0;
}
/*
output:
0000000000000000
000002B7B3386010:10
*/
reset
如果p是空,销毁该对象管理的内存;
如果p不是空,销毁该对象管理的内存,并且该对象重新获取p的所有权;
void reset (pointer p = pointer()) noexcept;
#include <iostream>
#include <memory>
int main()
{
std::unique_ptr<int> foo(new int);
*foo = 10;
{
std::cout << "before foo calls reset:" << foo.get() << " : " << *foo << "\n"; // 有效地址
foo.reset(); //删除该指针管理的对象
std::cout << "after foo calls reset:" << foo.get() << "\n"; // 无效地址
foo.reset(new int); //获取新的指针
*foo = 11;
std::cout << "before foo calls reset(p):" << foo.get() << ":" << *foo << "\n"; // 有效地址
}
std::cin.get();
return 0;
}
/*
output:
before foo calls reset:0000020076BF6010 : 10
after foo calls reset:0000000000000000
before foo calls reset(p):0000020076BF6010:11
*/
swap
交互俩个对象的所有权(包括deleter)
void swap (unique_ptr& x) noexcept;
#include <iostream>
#include <memory>
int main()
{
{
std::unique_ptr<int> foo(new int);
std::unique_ptr<int> bar(new int);
*foo = 66;
*bar = 99;
std::cout << "before foo calls swap:" << foo.get() << " : " << *foo << "\n";
std::cout << "before bar calls swap:" << bar.get() << " : " << *bar << "\n";
foo.swap(bar);
std::cout << "after foo calls swap:" << foo.get() << " : " << *foo << "\n";
std::cout << "after bar calls swap:" << bar.get() << " : " << *bar << "\n";
}
std::cin.get();
return 0;
}
/*
output:
before foo calls swap:0000019734716010 : 66
before bar calls swap:0000019734716050 : 99
after foo calls swap:0000019734716050 : 99
after bar calls swap:0000019734716010 : 66
*/
operator=
获取对象的所有权和删除器(右值将被析构,所有权都转移,就像调用reset()方法一样)。
unique_ptr& operator= (unique_ptr&& x) noexcept; // 移动赋值
unique_ptr& operator= (nullptr_t) noexcept; // 赋值nullptr
template <class U, class E>
unique_ptr& operator= (unique_ptr<U,E>&& x) noexcept; // 类型转换赋值
unique_ptr& operator= (const unique_ptr&) = delete; // 拷贝赋值
#include <iostream>
#include <memory>
int main()
{
{
std::unique_ptr<int> foo;
std::unique_ptr<int> boo(nullptr); // 赋值nullptr
foo = std::unique_ptr<int>(new int(96)); // 类型转换赋值
std::cout << "[foo]" << foo.get() << ':' << *foo << '\n';
boo = std::move(foo); // 移动赋值
std::cout << "[boo]" << boo.get() << ':' << *boo << '\n';
}
std::cin.get();
return 0;
}
/*
[foo]00000163BD716010:96
[boo]00000163BD716010:96
*/
operator bool
检测管理对象的指针是否为空(get()!=nullptr)。
explicit operator bool() const noexcept;
#include <iostream>
#include <memory>
int main()
{
{
std::unique_ptr<int> foo(new int);
std::unique_ptr<int> bar;
std::cout << "foo is " << (foo ? "not empty!\n" : "empty\n");
std::cout << "bar is " << (bar ? "not empty!\n" : "empty\n");
}
std::cin.get();
return 0;
}
/*
output:
foo is not empty!
bar is empty
*/
练习
#include <iostream>
#include <memory>
using namespace std;
class TestPtr {
public:
TestPtr() :name("default")
{
std::cout << __func__ << " : " << name << "\n";
}
TestPtr(const char *_name) :name(_name)
{
std::cout << __func__ << " : " << name << "\n";
}
~TestPtr()
{
std::cout << __func__ << " : " << name << "\n";
}
void PrintName(void)
{
std::cout << "Name:" << name << "\n";
}
private:
std::string name;
};
void Test()
{
TestPtr* p4(nullptr);
int i = 1;
{
std::unique_ptr<TestPtr> p3(new TestPtr("p3"));
{
std::unique_ptr<TestPtr> p0(new TestPtr("p0")); // 方式一
std::cout << '[' << i++ << "] p0:" << p0.get() << "\n";
std::unique_ptr<TestPtr> p1 = std::move(p0); // 移动p0的对象到p1, 内存不释放
std::cout << '[' << i++ << "] after p0 calls move :" << p0.get() << "\n"; // 无效地址:唯一指针,转移后为本身指向nullptr
std::cout << '[' << i++ << "] after p0 is moved to p1:" << p1.get() << "\n"; // 有效地址:指向p0原来指向的地址
p1.reset(); // 主动释放p1的内存
std::cout << '[' << i++ << "] after reset p1:" << p1.get() << "\n"; // 无效地址:主动调用析构释放内存
std::unique_ptr<TestPtr> p2 = std::make_unique<TestPtr>("p2"); // 方式二
std::cout << '[' << i++ << "] p2:" << p2.get() << "\n";
p4 = p3.release(); // 释放p3对象存储内存所有权,返回给p4
p3 = std::move(p2); // 移动p2的对象到p3, 对象的内存不释放,p3的对象内存自动释放
std::cout << '[' << i++ << "] after p2 calls move :" << p2.get() << "\n"; // 无效地址:唯一指针,转移后为本身指向nullptr
std::cout << '[' << i++ << "] after p2 is moved to p3:" << p3.get() << "\n"; // 有效地址:指向p2原来指向的地址
} // p3 内存不释放
p3->PrintName(); // name:p2
p4->PrintName(); // name: p3
} // 离开作用域后析构函数自动释放 p3的内存
if (p4)
{
std::cout << "p4 is not empty ptr, need manual delete it!\n";
delete p4; // 手动释放p3对象的内存,不释放就内存泄露
p4 = nullptr;
}
else
{
std::cout << "p4 is empty ptr,good!\n";
}
}
int main()
{
Test();
std::cin.get();
return 0;
}
/*
output:
TestPtr : p3
TestPtr : p0
[1] p0:0000014A1968D580
[2] after p0 calls move :0000000000000000
[3] after p0 is moved to p1:0000014A1968D580
~TestPtr : p0
[4] after reset p1:0000000000000000
TestPtr : p2
[5] p2:0000014A1968D580
[6] after p2 calls move :0000000000000000
[7] after p2 is moved to p3:0000014A1968D580
Name:p2
Name:p3
~TestPtr : p2
p4 is not empty ptr, need manual delete it!
~TestPtr : p3
*/