C++智能指针之unique_ptr

本文详细介绍了C++中智能指针unique_ptr的使用,包括其构造函数、析构函数、get、get_deleter、release、reset、swap、赋值运算符以及bool转换运算符等成员函数。unique_ptr作为独占所有权的智能指针,不允许复制,仅支持移动操作,确保了资源的有效管理。通过实例展示了unique_ptr如何管理动态分配的对象,并强调了其在内存管理中的重要性。
摘要由CSDN通过智能技术生成

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
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值