a review at smart pointer(2)

接下来详细介绍一下unique ptr的用法,之前参考的是msdn,因为网站写的很漂亮,react的组件,还是ms写的(ms信仰加成),但是最近发现ms的例子真是给的是黑魔法(C#的还好些,CPP真是一言难尽),可能是写文档的人段位太高不懂得照顾新人。。吧,于是太detail的部分还是得按照cpp官网的来

ref http://www.cplusplus.com/reference/memory/unique_ptr/unique_ptr/

  • 首先第一个例子
// unique_ptr constructor example
#include <iostream>
#include <memory>

int main () {
  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;
}

我们发现它的输出是

u1: null
u2: null
u3: not null
u4: not null
u5: null
u6: null
u7: not null
u8: not null
  • u1和u2是空,很好理解。
  • u3 u4 是int* 的unique ptr,不是空,也很好理解

引申出一个话题,什么是std::default_delete d ???

  • std::default_delete is the default destruction policy used by std::unique_ptr when no deleter is specified.
  1. The non-specialized default_delete uses delete to deallocate memory for a single object.

  2. A partial specialization for array types that uses delete[] is also provided.

  • 原型是template< class T > struct default_delete和template< class T > struct default_delete<T[]>
  • 所以我们得知,要么用默认的default delete,要么不写空着,不写空着也是给你默认是default delete,要么自己定制deleter。
  • u5, u6, u7, u8都很好理解不解释了,auto 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]
}
  • 这个代码很复杂,涉及lambda表达式和模版,此程序自定义了一个deleter给一个智能指针,<>的第一个参数是int,也就是int* 指针,第二个是一个decltype(deleter),decltype的用法基本等同于auto,都是编译时确定这个东西的类型,区别是auto要用这个变量,decltype推完了就拉倒,这个deleter是一个lambda class,至于细节部分,看不懂不管了。https://docs.microsoft.com/en-us/cpp/standard-library/unique-ptr-class?view=msvc-160 这里面有contructor的详细声明。

接下来是operator=的部分

// unique_ptr::operator= example
#include <iostream>
#include <memory>

int main () {
  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;
}
  • 这段代码麻烦的地方在于rvalue。虽然unique ptr的拷贝构造函数和operator=都被设计成了delete,但是它有move构造,所以传参数,赋值,返回都没问题(这里有歧义)
auto w = std::make_unique<Widget>();
auto w2 = w; // 编译错误
  • 上面的就是错的,只能move,估计因为左边的不是null
  • 此代码第一个输出empty,第二个输出101

get

  • 很好理解不解释了,get获取那个裸指针,release是放空一个smart pointer里的raw pointer让其变成null
// unique_ptr::get vs unique_ptr::release
#include <iostream>
#include <memory>

int main () {
                                           // foo   bar    p
                                           // ---   ---   ---
  std::unique_ptr<int> foo;                // null
  std::unique_ptr<int> bar;                // null  null
  int* p = nullptr;                        // null  null  null

  foo = std::unique_ptr<int>(new int(10)); // (10)  null  null
  bar = std::move(foo);                    // null  (10)  null
  p = bar.get();                           // null  (10)  (10)
  *p = 20;                                 // null  (20)  (20)
  p = nullptr;                             // null  (20)  null

  foo = std::unique_ptr<int>(new int(30)); // (30)  (20)  null
  p = foo.release();                       // null  (20)  (30)
  *p = 40;                                 // null  (20)  (40)

  std::cout << "foo: ";
  if (foo) std::cout << *foo << '\n'; else std::cout << "(null)\n";

  std::cout << "bar: ";
  if (bar) std::cout << *bar << '\n'; else std::cout << "(null)\n";

  std::cout << "p: ";
  if (p) std::cout << *p << '\n'; else std::cout << "(null)\n";
  std::cout << '\n';

  delete p;   // the program is now responsible of deleting the object pointed to by p
              // bar deletes its managed object automatically

  return 0;
}

get_deleter

// unique_ptr deleter with state
#include <iostream>
#include <memory>

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 main () { 
  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;
}
  • 这一段代码解释了如何自定义构造一个deleter class,这个deleter class有一个template void operator()(T* p)的运算符重载,它的用法在底下可以看见某指针.get_deleter()(new int)的格式,我们看出new int对应的是模版里的T* p,这个删除器重载()的意思就是删除一个T* p。
  • 然后这个复杂的例子解释的是我们可以传递一个deleter进入这个指针,也可以不传递一个deleter进入这个指针,假设你在<>声明了这个deleter,那么假设不穿参数,那就是此deleter的默认(一个新值,而不是引用),假设不在<>声明deleter,然后就只能传std::default_deleter,假设声明且传参,那就是完全的自定义。
  • 还有一点值得注意的是,可以在<>声明引用,那么()里的就会当成ref。

operator bool重载

// example of unique_ptr::operator bool
#include <iostream>
#include <memory>


int main () {
  std::unique_ptr<int> foo;
  std::unique_ptr<int> bar (new int(12));

  if (foo) std::cout << "foo points to " << *foo << '\n';
  else std::cout << "foo is empty\n";

  if (bar) std::cout << "bar points to " << *bar << '\n';
  else std::cout << "bar is empty\n";

  return 0;
}
  • 将一个智能指针当成bool

release

// unique_ptr::release example
#include <iostream>
#include <memory>

int main () {
  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;
}
  • 这个例子输出的是10,我们可以看出,release的目的是 Releases ownership of its stored pointer, by returning its value and replacing it with a null pointer. This call does not destroy the managed object,也就是将裸指针设为null,但是它的返回值是裸指针设为null之前的值

reset

// unique_ptr::reset example
#include <iostream>
#include <memory>

int main () {
  std::unique_ptr<int> up;  // empty

  up.reset (new int);       // takes ownership of pointer
  *up=5;
  std::cout << *up << '\n';

  up.reset (new int);       // deletes managed object, acquires new pointer
  *up=10;
  std::cout << *up << '\n';

  up.reset();               // deletes managed object

  return 0;
}
  • 我们可以发现,reset()接受一个裸指针,并销毁原先的指针,把封装指针变成参数里的那个。

swap

// unique_ptr swap specialization
#include <iostream>
#include <memory>

int main () {
  std::unique_ptr<int> foo (new int(10));
  std::unique_ptr<int> bar (new int(20));

  swap(foo,bar);

  std::cout << "foo: " << *foo << '\n';
  std::cout << "bar: " << *bar << '\n';

  return 0;
}
  • 这是非成员函数的swap,下面的是成员函数的swap
// unique_ptr::swap example
#include <iostream>
#include <memory>

int main () {
  std::unique_ptr<int> foo (new int(10));
  std::unique_ptr<int> bar (new int(20));

  foo.swap(bar);

  std::cout << "foo: " << *foo << '\n';
  std::cout << "bar: " << *bar << '\n';

  return 0;
}
  • swap的目的是交换指针的所有权,foo.swap(bar)

operator* operator->

// unique_ptr::operator*
#include <iostream>
#include <memory>

int main () {
  std::unique_ptr<int> foo (new int);
  std::unique_ptr<int> bar (new int (100));

  *foo = *bar * 2;

  std::cout << "foo: " << *foo << '\n';
  std::cout << "bar: " << *bar << '\n';

  return 0;
}
  • 运算符重载 * ,返回A reference to the object pointed.
// unique_ptr::operator->
#include <iostream>
#include <memory>

struct C { int a; int b; };

int main () {
  std::unique_ptr<C> foo (new C);
  std::unique_ptr<C> bar;

  foo->a = 10;
  foo->b = 20;

  bar = std::move(foo);

  if (foo) std::cout << "foo: " << foo->a << ' ' << foo->b << '\n';
  if (bar) std::cout << "bar: " << bar->a << ' ' << bar->b << '\n';

  return 0;
}
  • 运算符重载 -> , 返回A pointer to the object managed by the unique_ptr.

operator[]

// unique_ptr::operator[]
#include <iostream>
#include <memory>

int main () {
  std::unique_ptr<int[]> foo (new int[5]);

  for (int i=0; i<5; ++i) foo[i] = i;

  for (int i=0; i<5; ++i) std::cout << foo[i] << ' ';
  std::cout << '\n';

  return 0;
}
posted @ 2021-01-04 15:19  五个桔核  阅读(88)  评论(0)    收藏  举报