uniqut_ptr 是一种对资源具有排他性拥有权的智能指针, 即一个对象资源只能同时被一个 unique_ptr 指向.
一, 初始化方式
通过 new 云算法或者普通指针
unique_ptr<Investment> up(new Investment());
或者
- Investment *pInv = new Investment();
- unique_ptr<Investment> up1(pInv);
通过 make_unique
auto pInv = make_unique<Investment>();
通过 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++11 中 unique_ptr 的使用
在 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 中文版):
下面是从其他文章中 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 0;
- }
- //////////////////////////////////////////////////////
- // 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 0;
- }
- //////////////////////////////////////////////////////
- // 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 0;
- }
- ////////////////////////////////////////////////////
- // 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(3));
- 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[5]);
- arrup[0] = 5;
- // 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(0))); // populate vector
- vi.push_back(std::unique_ptr<int>(new int(3)));
- vi.push_back(std::unique_ptr<int>(new int(2)));
- std::sort(vi.begin(), vi.end()); // result: {0, 2, 3}
- return 0;
- }
- //////////////////////////////////////////////////////////////////
- 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{ 10 }, b{ 5 };
- std::cout <<tt->add_sub(a, b) <<std::endl;
- return 0;
- }
- //////////////////////////////////////////////////////////////////
- int test_unique_ptr6()
- {
- std::unique_ptr<int[]> tmp(new int[100]);
- std::for_each(tmp.get(), tmp.get() + 100, [](int& n) {n = 66; });
- std::cout <<tmp[99] << std::endl;
- return 0;
- }
- ///////////////////////////////////////////////////////////////////////
- // 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[3] };
- } // calls ~D 3 times
- return 0;
- }
- /////////////////////////////////////////////////////////////
- // 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 0; // [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_(0) {}
- 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 0;
- }
- //////////////////////////////////////////////////////////////
- // 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(101)); // 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 0;
- }
- /////////////////////////////////////////////////////////////////
- // 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 = 10;
- manual_pointer = auto_pointer.release();
- // (auto_pointer is now empty)
- std::cout <<"manual_pointer points to" << *manual_pointer << '\n';
- delete manual_pointer;
- return 0;
- }
- ///////////////////////////////////////////////////////////////
- // reference: http://www.cplusplus.com/reference/memory/unique_ptr/swap/
- int test_unique_ptr12()
- {
- std::unique_ptr<int> foo(new int(10)), foo2(new int(10));
- std::unique_ptr<int> bar(new int(20)), bar2(new int(20));
- 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 0;
- }
- ////////////////////////////////////////////////////////////////
- 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 -1;
- }
- *A = 10;
- }
- {
- typedef std::unique_ptr<int, std::function<void(int*)>> Oper;
- Oper A(new int, math_add);
- *A = 10;
- Oper B(new int, math_subtract);
- *B = 10;
- }
- return 0;
- }
- } // namespace unique_ptr_
- View Code
来源: http://www.bubuko.com/infodetail-3333603.html