C++11智能指针之std::unique_ptr

时间:2023-03-09 09:25:51
C++11智能指针之std::unique_ptr

uniqut_ptr是一种对资源具有排他性拥有权的智能指针,即一个对象资源只能同时被一个unique_ptr指向。

一、初始化方式

  1. 通过new云算法或者普通指针

unique_ptr<Investment> up(new Investment());

或者

Investment *pInv = new Investment();

unique_ptr<Investment> up1(pInv);

  1. 通过make_unique

auto pInv = make_unique<Investment>();

  1. 通过move()函数

    unique_ptr<Investment> up1 = std::move(up);

注意:unique_ptr不能被复制或者拷贝,下面的代码将出错:

unique_ptr<Investment> up(new Investment()); //ok

unique_ptr<Investment> up1(up);              //error, can not be copy

unique_ptr<Investment> up2 = up;            //error, can not be assigned

但是,unique_ptr可以作为函数的返回值:

unique_ptr<Investment> GetPtr();        //function getthe unique pointer

unique_ptr<Investment> pInv = GetPtr(); // ok

二、自定义释放器

用如下方式使用带自定义资源释放的unique_ptr

auto delete_Investment = [](Investment* pInv)

{

pInv->getObjectType();

delete pInv;

};

unique_ptr<Investment,decltype(delete_Investment)> pInvest(nullptr,delete_Investment);

或者也可以使用函数指针

void deleteInv(Investment* pInv) {}

std::unique_ptr<Investment,void(*)(Investment*)>ptr(nullptr,deleteInv) ;

三、 unique_ptr 基本操作

unique_ptr<Investment> pInvestment;     // 创建一个空的智能指针

pInvestment.reset(new Investment());    //"绑定”动态对象

Investment *pI = pInvestment.release(); //释放所有权

pI= nullptr;                         //显式销毁所指对象,同时智能指针变为空指针。

四、管理动态数组

由于unique_ptr有std::unique_ptr<T[]>的重载函数,所以它可以用来管理数组资源

unique_ptr<int[]> pArray(new int[3]{1,3,3});

在C++中,动态内存的管理是通过一对运算符来完成的:new,在动态内存中为对象分配空间并返回一个指向该对象的指针,可以选择对对象进行初始化;delete,接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。

动态内存的使用很容易出问题,因为确保在正确的时间释放内存是极其困难的。有时会忘记释放内存,在这种情况下会产生内存泄露;有时在尚有指针引用内存的情况下就释放了它,在这种情况下就会产生引用非法内存的指针。

为了更容易(同时也更安全)地使用动态内存,C++11标准库提供了两种智能指针(smart pointer)类型来管理动态对象。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指的对象。C++11标准库提供的这两种智能指针的区别在于管理底层指针的方式:shared_ptr允许多个指针指向同一个对象;unique_ptr则"独占"所指向的对象。C++11标准库还定义了一个名为weak_ptr的辅助类,它是一种弱引用,指向shared_ptr所管理的对象。这三种类型都定义在memory头文件中。智能指针是模板类而不是指针。类似vector,智能指针也是模板,当创建一个智能指针时,必须提供额外的信息即指针可以指向的类型。默认初始化的智能指针中保存着一个空指针。智能指针的使用方式与普通指针类似。解引用一个智能指针返回它指向的对象。如果在一个条件判断中使用智能指针,效果就是检测它是否为空。

In C++, a smart pointer is implemented as a template class that mimics, by means of operator overloading, the behaviors of a traditional (raw) pointer, (e.g. dereferencing, assignment) while providing additional memory management features.

std::unique_ptr is a smart pointer that retains sole ownership of an object through a pointer and destroys that object when the unique_ptr goes out of scope. No two unique_ptr instances can manage the same object.

The object is destroyed and its memory deallocated when either of the following happens: (1)、the managing unique_ptr object is destroyed; (2)、the managing unique_ptr object is assigned another pointer via operator= or reset().

A unique_ptr may alternatively own no object, in which case it is called empty.

Only non-const unique_ptr can transfer the ownership of the managed object to another unique_ptr. The lifetime of an object managed by const std::unique_ptr is limited to the scope in which the pointer was created.

std::unique_ptr may be constructed for an incomplete type T. Conversely, std::shared_ptr can't be constructed from a raw pointer to incomplete type, but can be destroyed where T is incomplete.

A unique_ptr does not share its pointer. It cannot be copied to another unique_ptr, passed by value to a function, or used in any Standard Template Library (STL) algorithm that requires copies to be made. A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it.

When using unique_ptr, there can be at most one unique_ptr pointing at any one resource.When that unique_ptr is destroyed, the resource is automatically reclaimed.Because there can only be one unique_ptr to any resource, any attempt to make a copy of a unique_ptr will cause a compile-time error. However, unique_ptr can be moved using the new move semantics.

shared_ptr, allows for multiple pointers to point at a given resource. When the very last shared_ptr to a resource is destroyed, the resource will be deallocated. shared_ptr uses reference counting to track how many pointers refer to a resource, so you need to be careful not to introduce any reference cycles.

A unique_ptr is a container for a raw pointer, which the unique_ptr is said to own. A unique_ptr explicitly prevents copying of its contained pointer (as would happen with normal assignment), but the std::move function can be used to transfer ownership of the contained pointer to another unique_ptr. A unique_ptr cannot be copied because its copy constructor and assignment operators are explicitly deleted.

everything you can do with auto_ptr, unique_ptr will do as well.

There are two kinds of unique_ptr, one for scalars (i.e. non-arrays) and one for arrays:

(1)、unique_ptr<double> can hold a scalar of type double;

(2)、unique_ptr<double[]> can hold an array of double values with an unknown number of elements.

std::unique_ptr是C++11标准中用来取代std::auto_ptr的指针容器(在C++11中,auto_ptr被废弃)。它不能与其它unique_ptr类型的指针对象共享所指对象的内存。这种”所有权”仅能够通过标准库的move函数来转移。unique_ptr是一个删除了拷贝构造函数、保留了移动构造函数的指针封装类型。

一个unique_ptr"拥有"它所指向的对象。与shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定对象。当unique_ptr被销毁时,它所指向的对象也被销毁。与shared_ptr不同,在C++11中,没有类似make_shared的标准库函数返回一个unique_ptr。当定义一个unique_ptr时,需要将其绑定到一个new返回的指针上。类似shared_ptr,初始化unique_ptr必须采用直接初始化形式。由于一个unique_ptr拥有它指向的对象,因此unique_ptr不支持普通的拷贝或赋值操作。虽然不能拷贝或赋值unique_ptr,但可以通过调用release或reset将指针的所有权从一个(非const)unique_ptr转移给另一个unique。

调用release会切断unique_ptr和它原来管理的对象间的联系。release返回的指针通过被用来初始化另一个智能指针或给另一个智能指针赋值。如果不用另一个智能指针来保存release返回的指针,程序就要负责资源的释放。

不能拷贝unique_ptr的规则有一个例外:我们可以拷贝或赋值一个将要被销毁的unique_ptr,最常见的例子是从函数返回一个unique_ptr。

类似shared_ptr,unique_ptr默认情况下用delete释放它指向的对象。与shared_ptr一样,可以重载一个unique_ptr中默认的删除器。但是,unique_ptr管理删除器的方式与shared_ptr不同。

下图列出了unique_ptr支持的操作(来源于C++ Primer Fifth Edition 中文版):

C++11智能指针之std::unique_ptr

下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:

#include "unique_ptr.hpp"
#include <iostream>
#include <memory>
#include <string>
#include <cstdlib>
#include <utility>
#include <vector>
#include <algorithm>
#include <cassert>
#include <fstream>
#include <functional> namespace unique_ptr_ { ///////////////////////////////////////////////////////
// reference: http://en.cppreference.com/w/cpp/memory/unique_ptr
struct Foo
{
Foo() { std::cout << "Foo::Foo\n"; }
~Foo() { std::cout << "Foo::~Foo\n"; }
void bar() { std::cout << "Foo::bar\n"; }
}; void f(const Foo &)
{
std::cout << "f(const Foo&)\n";
} int test_unique_ptr1()
{
std::unique_ptr<Foo> p1(new Foo); // p1 owns Foo
if (p1) p1->bar(); {
std::unique_ptr<Foo> p2(std::move(p1)); // now p2 owns Foo
f(*p2); p1 = std::move(p2); // ownership returns to p1
std::cout << "destroying p2...\n";
} if (p1) p1->bar(); // Foo instance is destroyed when p1 goes out of scope return ;
} //////////////////////////////////////////////////////
// reference: http://www.cplusplus.com/reference/memory/unique_ptr/unique_ptr/
int test_unique_ptr2()
{
std::default_delete<int> d;
std::unique_ptr<int> u1;
std::unique_ptr<int> u2(nullptr);
std::unique_ptr<int> u3(new int);
std::unique_ptr<int> u4(new int, d);
std::unique_ptr<int> u5(new int, std::default_delete<int>());
std::unique_ptr<int> u6(std::move(u5));
std::unique_ptr<int> u7(std::move(u6));
std::unique_ptr<int> u8(std::auto_ptr<int>(new int)); std::cout << "u1: " << (u1 ? "not null" : "null") << '\n';
std::cout << "u2: " << (u2 ? "not null" : "null") << '\n';
std::cout << "u3: " << (u3 ? "not null" : "null") << '\n';
std::cout << "u4: " << (u4 ? "not null" : "null") << '\n';
std::cout << "u5: " << (u5 ? "not null" : "null") << '\n';
std::cout << "u6: " << (u6 ? "not null" : "null") << '\n';
std::cout << "u7: " << (u7 ? "not null" : "null") << '\n';
std::cout << "u8: " << (u8 ? "not null" : "null") << '\n'; return ;
} //////////////////////////////////////////////////////
// reference: http://eli.thegreenplace.net/2012/06/20/c11-using-unique_ptr-with-standard-library-containers
struct Foo_0 {
Foo_0() { std::cerr << "Foo_0 [" << this << "] constructed\n"; }
virtual ~Foo_0() { std::cerr << "Foo_0 [" << this << "] destructed\n"; }
}; void sink(std::unique_ptr<Foo_0> p) {
std::cerr << "Sink owns Foo_0 [" << p.get() << "]\n";
} std::unique_ptr<Foo_0> source() {
std::cerr << "Creating Foo_0 in source\n";
return std::unique_ptr<Foo_0>(new Foo_0);
} int test_unique_ptr3()
{
std::cerr << "Calling source\n";
std::unique_ptr<Foo_0> pmain = source(); // Can also be written as
// auto pmain = source(); std::cerr << "Now pmain owns Foo [" << pmain.get() << "]\n";
std::cerr << "Passing it to sink\n";
// sink(pmain); // ERROR! can't copy unique_ptr
sink(move(pmain)); // OK: can move it! std::cerr << "Main done\n";
return ;
} ////////////////////////////////////////////////////
// reference: http://www.codeguru.com/cpp/article.php/c17775/The-Smart-Pointer-That-Makes-Your-C-Applications-Safer--stduniqueptr.htm
void func(int*)
{ } int test_unique_ptr4()
{
// default construction
std::unique_ptr<int> up; //creates an empty object // initialize with an argument
std::unique_ptr<int> uptr(new int());
double *pd = new double;
std::unique_ptr<double> uptr2(pd);
// overloaded * and ->
*uptr2 = 23.5;
std::unique_ptr<std::string> ups(new std::string("hello"));
int len = ups->size(); // Reset() releases the owned resource and optionally acquires a new resource:
uptr2.reset(new double); //delete pd and acquire a new pointer
uptr2.reset(); //delete the pointer acquired by the previous reset() call // If you need to access the owned pointer directly use get()
func(uptr.get()); // Unique_ptr has implicit conversion to bool.
// This lets you use unique_ptr object in Boolean expressions such as this:
if (ups) //implicit conversion to bool
std::cout << *ups << std::endl;
else
std::cout << "an empty smart pointer" << std::endl; // Array Support: Unique_ptr can store arrays as well.
// A unique_ptr that owns an array defines an overloaded operator [].
// Obviously, the * and -> operators are not available.
// Additionally, the default deleter calls delete[] instead of delete:
std::unique_ptr<int[]> arrup(new int[]);
arrup[] = ;
// std::cout << *arrup << std::endl; //error, operator * not defined // Compatibility with Containers and Algorithms
// You can safely store unique_ptr in Standard Library containers and let algorithms manipulate sequences of unique_ptr objects.
std::vector<std::unique_ptr<int>> vi;
vi.push_back(std::unique_ptr<int>(new int())); // populate vector
vi.push_back(std::unique_ptr<int>(new int()));
vi.push_back(std::unique_ptr<int>(new int()));
std::sort(vi.begin(), vi.end()); // result: {0, 2, 3} return ;
} //////////////////////////////////////////////////////////////////
template <typename T>
class Add {
public:
T add_sub(T a, T b)
{
return (a + b) * (a - b);
}
}; int test_unique_ptr5()
{
std::unique_ptr<Add<int>> tt(new Add<int>());
int a{ }, b{ }; std::cout << tt->add_sub(a, b) << std::endl; return ;
} //////////////////////////////////////////////////////////////////
int test_unique_ptr6()
{
std::unique_ptr<int[]> tmp(new int[]);
std::for_each(tmp.get(), tmp.get() + , [](int& n) {n = ; });
std::cout << tmp[] << std::endl; return ;
} ///////////////////////////////////////////////////////////////////////
// reference: https://en.cppreference.com/w/cpp/memory/unique_ptr
struct B {
virtual void bar() { std::cout << "B::bar\n"; }
virtual ~B() = default;
};
struct D : B
{
D() { std::cout << "D::D\n"; }
~D() { std::cout << "D::~D\n"; }
void bar() override { std::cout << "D::bar\n"; }
}; // a function consuming a unique_ptr can take it by value or by rvalue reference
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{
p->bar();
return p;
} void close_file(std::FILE* fp) { std::fclose(fp); } int test_unique_ptr7()
{
std::cout << "unique ownership semantics demo\n";
{
auto p = std::make_unique<D>(); // p is a unique_ptr that owns a D
auto q = pass_through(std::move(p));
assert(!p); // now p owns nothing and holds a null pointer
q->bar(); // and q owns the D object
} // ~D called here std::cout << "Runtime polymorphism demo\n";
{
std::unique_ptr<B> p = std::make_unique<D>(); // p is a unique_ptr that owns a D
// as a pointer to base
p->bar(); // virtual dispatch std::vector<std::unique_ptr<B>> v; // unique_ptr can be stored in a container
v.push_back(std::make_unique<D>());
v.push_back(std::move(p));
v.emplace_back(new D);
for (auto& p : v) p->bar(); // virtual dispatch
} // ~D called 3 times std::cout << "Custom deleter demo\n";
std::ofstream("demo.txt") << 'x'; // prepare the file to read
{
std::unique_ptr<std::FILE, decltype(&close_file)> fp(std::fopen("demo.txt", "r"), &close_file);
if (fp) // fopen could have failed; in which case fp holds a null pointer
std::cout << (char)std::fgetc(fp.get()) << '\n';
} // fclose() called here, but only if FILE* is not a null pointer
// (that is, if fopen succeeded) std::cout << "Custom lambda-expression deleter demo\n";
{
std::unique_ptr<D, std::function<void(D*)>> p(new D, [](D* ptr)
{
std::cout << "destroying from a custom deleter...\n";
delete ptr;
}); // p owns D
p->bar();
} // the lambda above is called and D is destroyed std::cout << "Array form of unique_ptr demo\n";
{
std::unique_ptr<D[]> p{ new D[] };
} // calls ~D 3 times return ;
} /////////////////////////////////////////////////////////////
// reference: http://www.cplusplus.com/reference/memory/unique_ptr/~unique_ptr/
int test_unique_ptr8()
{
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 ; // [deleter called]
} /////////////////////////////////////////////////////////////////////
// reference: http://www.cplusplus.com/reference/memory/unique_ptr/get_deleter/
class state_deleter { // a deleter class with state
int count_;
public:
state_deleter() : count_() {}
template <class T>
void operator()(T* p) {
std::cout << "[deleted #" << ++count_ << "]\n";
delete p;
}
}; int test_unique_ptr9()
{
state_deleter del; std::unique_ptr<int> p; // uses default deleter // alpha and beta use independent copies of the deleter:
std::unique_ptr<int, state_deleter> alpha(new int);
std::unique_ptr<int, state_deleter> beta(new int, alpha.get_deleter()); // gamma and delta share the deleter "del" (deleter type is a reference!):
std::unique_ptr<int, state_deleter&> gamma(new int, del);
std::unique_ptr<int, state_deleter&> delta(new int, gamma.get_deleter()); std::cout << "resetting alpha..."; alpha.reset(new int);
std::cout << "resetting beta..."; beta.reset(new int);
std::cout << "resetting gamma..."; gamma.reset(new int);
std::cout << "resetting delta..."; delta.reset(new int); std::cout << "calling gamma/delta deleter...";
gamma.get_deleter()(new int); alpha.get_deleter() = state_deleter(); // a brand new deleter for alpha // additional deletions when unique_ptr objects reach out of scope
// (in inverse order of declaration) return ;
} //////////////////////////////////////////////////////////////
// reference: http://www.cplusplus.com/reference/memory/unique_ptr/operator=/
int test_unique_ptr10()
{
std::unique_ptr<int> foo;
std::unique_ptr<int> bar; foo = std::unique_ptr<int>(new int()); // rvalue bar = std::move(foo); // using std::move std::cout << "foo: ";
if (foo) std::cout << *foo << '\n'; else std::cout << "empty\n"; std::cout << "bar: ";
if (bar) std::cout << *bar << '\n'; else std::cout << "empty\n"; return ;
} /////////////////////////////////////////////////////////////////
// reference: http://www.cplusplus.com/reference/memory/unique_ptr/release/
int test_unique_ptr11()
{
std::unique_ptr<int> auto_pointer(new int);
int * manual_pointer; *auto_pointer = ; manual_pointer = auto_pointer.release();
// (auto_pointer is now empty) std::cout << "manual_pointer points to " << *manual_pointer << '\n'; delete manual_pointer; return ; } ///////////////////////////////////////////////////////////////
// reference: http://www.cplusplus.com/reference/memory/unique_ptr/swap/
int test_unique_ptr12()
{
std::unique_ptr<int> foo(new int()), foo2(new int());
std::unique_ptr<int> bar(new int()), bar2(new int()); foo.swap(bar);
std::cout << "foo: " << *foo << '\n';
std::cout << "bar: " << *bar << '\n'; std::swap(foo2, bar2);
std::cout << "foo2: " << *foo2 << '\n';
std::cout << "bar2: " << *bar2 << '\n'; return ;
} ////////////////////////////////////////////////////////////////
void math_add(int* a)
{
int b = ++(*a);
delete a;
fprintf(stdout, "add operation: %d\n", b);
} void math_subtract(int* a)
{
int b = --(*a);
delete a;
fprintf(stdout, "subtraction operation: %d\n", b);
} int test_unique_ptr13()
{
{
std::unique_ptr<int, decltype(&math_add)> A(new int, &math_add);
if (!A) {
fprintf(stderr, "A is nullptr\n");
return -;
} *A = ;
} {
typedef std::unique_ptr<int, std::function<void(int*)>> Oper; Oper A(new int, math_add);
*A = ; Oper B(new int, math_subtract);
*B = ;
} return ;
} } // namespace unique_ptr_