[C++] Vector Changing Size allocator 、RAII 、 unique_ptr

Vector Changing size

[C++] Vector Changing Size allocator 、RAII 、 unique_ptr_第1张图片
Vector Changing size

ordinary reserve()

void vector::reserve(int newalloc)
{
  if (newalloc<=space) return; // never decrease allocation
  double* p = new double[newalloc]; // allocate new space
  for (int i=0; i

ordinary resize()

void vector::resize(int newsize)
// make the vector have newsize elements
// initialize each new element with the default value 0.0
{
  reserve(newsize);
  for (int i=sz; i

ordinary push_back()

void vector::push_back(double d)
// increase vector size by one; initialize the new element with d
{
  if (space==0)
  reserve(8); // start with space for 8 elements
  else if (sz==space)
  reserve(2*space); // get more space
  elem[sz] = d; // add d at end
  ++sz; // increase the size (sz is the number of elements)
}

allocator

deallocate()是将分配出的空间(没有被使用的空间) 放回主存。destroy()是将已经被构造的空间(已有值) 析构成没有被使用的空间放回内存池[2]

template class allocator {
public:
// . . .
  T* allocate(int n); // allocate space for n objects of type T
  void deallocate(T* p, int n); // deallocate n objects of type T starting at p
  void construct(T* p, const T& v); // construct a T with the value v in p
  void destroy(T* p); // destroy the T in p
};

vector

template> class vector {
  A alloc; // use allocate to handle memory for elements
  // . . .
};

vector::reserve()

template void vector::reserve(int newalloc)
{
  if (newalloc<=space) return; // never decrease allocation
  T* p = alloc.allocate(newalloc); // allocate new space
  for (int i=0; i

vector::resize()

template
void vector::resize(int newsize, T val = T())
{
  reserve(newsize);
  for (int i=sz; i

vector::push_back()

template
void vector::push_back(const T& val)
{
  if (space==0) reserve(8); // start with space for 8 elements
  else if (sz==space) reserve(2*space); // get more space
  alloc.construct(&elem[sz],val); // add val at end
  ++sz; // increase the size
}

make_vec()

try catch

vector* make_vec() // make a filled vector
{
  vector* p = new vector; // we allocate on free store
  try {
  // fill the vector with data; this may throw an exception
  return p;
  }
  catch (. . .) {
    delete p; // do our local cleanup
    throw; // re-throw to allow our caller to deal with the fact
    // that make_vec() couldn’t do what was
    // required of it
  }
}

unique_ptr vector*

vector* make_vec() // make a filled vector
{
  unique_ptr> p {new vector}; // allocate on free store
  // . . . fill the vector with data; this may throw an exception . . .
  return p.release(); // return the pointer held by p
}

unique_ptr unique_ptr

unique_ptr> make_vec() // make a filled vector
{
  unique_ptr> p {new vector}; // allocate on free store
  // . . . fill the vector with data; this may throw an exception . . .
  return p;
}

A unique_ptr is very much like an ordinary pointer, but it has one significant restriction: you cannot assign one unique_ptr to another to get two unique_ptrs to the same object.[1]

pLarge -> DoSomething();

智能指针是在 标头文件中的 std 命名空间中定义的,对 RAII (获取资源即初始化”编程惯用法)至关重要:

RAII主要目的是确保资源获取对象初始化 同时发生 ;
RAII主要原则是为将任何堆分配资源(例如,动态分配内存或系统对象句柄)的所有权提供给其析构函数包含用于删除或释放资源的代码以及任何相关清理代码的堆栈分配对象;

通过使用熟悉的指针运算符(-> 和 *)访问封装指针,智能指针类将重载这些运算符以返回封装的原始指针[5]

#include 
#include 

using namespace std;

class MyClass {
public:
    MyClass() { cout << "MyClass()\n";}
    void say_hello() {cout << "Hello!\n";}
    ~MyClass() { cout << "~MyClass()\n";}
};



int main()
{
    unique_ptr p_new;
    p_new->say_hello();
    
    MyClass* p_old;
    p_old->say_hello();
}
Hello!
Hello!

Return by moving

vector make_vec() // make a filled vector
{
  vector res;
  // . . . fill the vector with data; this may throw an exception . . .
  return res; // the move constructor efficiently transfers ownership
}

move

class vector {
  int sz;
  double* elem;

public:
  vector(vector&& a); // move constructor
  vector& operator=(vector&&); // move assignment
  // . . .
};

move constructor

vector::vector(vector&& a)
:sz{a.sz}, elem{a.elem} // copy a’s elem and sz
{
  a.sz = 0; // make a the empty vector
  a.elem = nullptr;
}

move assignment

vector& vector::operator=(vector&& a) // move a to this vector
{
  delete[] elem; // deallocate old space
  elem = a.elem; // copy a’s elem and sz
  sz = a.sz;
  a.elem = nullptr; // make a the empty vector
  a.sz = 0;
  return *this; // return a self-reference
}

RAII

This technique is usually referred to by the awkward phrase Resource Acquisition Is Initialization abbreviated to RAII.

vector::reserve

template
void vector::reserve(int newalloc)
{
  if (newalloc<=space) return; // never decrease allocation
  T* p = alloc.allocate(newalloc); // allocate new space
  for (int i=0; i

RAII for vector

vector_base

template
struct vector_base {
  A alloc; // allocator
  T* elem; // start of allocation
  int sz; // number of elements
  int space; // amount of allocated space
  vector_base(const A& a, int n)
  : alloc{a}, elem{alloc.allocate(n)}, sz{n}, space{n}{ }
  ~vector_base() { alloc.deallocate(elem,space); }
  };

vector : vector_base

template> 
class vector : private vector_base {
  public:
  // . . .
};

rewrite reserve()

template
void vector::reserve(int newalloc)
{
  if (newalloc<=this–>space) return; // never decrease allocation
  vector_base b(this–>alloc,newalloc); // allocate new space
  uninitialized_copy(b.elem,&b.elem[this–>sz],this–>elem); // copy
  for (int i=0; isz; ++i)
  this–>alloc.destroy(&this–>elem[i]); // destroy old
  swap>(*this,b); // swap representations
}

Similarly, we have to explicitly use this–> when we refer to a member of the base class vector_base from a member of the derived class vector, such as vector::reserve().

对比看oridinary 的reserve()和 RAII 的reserve()

deallocate()被放到了析构函数之中

vector::reserve
    alloc.deallocate(elem,space); // deallocate old space

struct vector_base
    ~vector_base() { alloc.deallocate(elem,space); }

allocate()被放到了构造函数之中

vector::reserve
 T* p = alloc.allocate(newalloc);


template void vector::reserve
    vector_base b(this–>alloc,newalloc);

struct vector_base
      T* elem; // start of allocation
          
          vector_base(const A& a, int n)
      : ... elem{alloc.allocate(n)}...{}

/* 可以看到新版本b.elem相当于原始版本的p */

复制操作的实现

for (int i=0; isz],this–>elem); // copy

/*
    b 是初始化的新的对象;
    b.elem相当于原始版本的p;
    uninitialized_copy(...)是STL函数;
*/

销毁已经被赋值的东西

for (int i=0; isz; ++i)
    this–>alloc.destroy(&this–>elem[i]); // destroy old
/*
    除了显式地写出来this-> 并没有什么区别
*/  

指向新分配的空间

elem = p;
space = newalloc;


swap>(*this,b); // swap representations
vector_base(const A& a, int n)
  : ... space{n}{ }

/*
    space = newoalloc 的赋值操作早在在构造函数中初始化成员变量时已经完成
*/  

reference

[1].Programming -- Principles and Practice Using C++ (Second Edition) http://www.stroustrup.com/Programming/
[2]. 有关allocator类的destroy和deallocate [A_LeiQ] 于 2017.04.09 15:24 提问
http://ask.csdn.net/questions/377223
[3]. 如何:创建和使用 unique_ptr 实例
https://msdn.microsoft.com/zh-cn/library/hh279676.aspx
[4].std::unique_ptr
http://zh.cppreference.com/w/cpp/memory/unique_ptr
[5] 智能指针 现代C++
https://msdn.microsoft.com/zh-cn/library/hh279674.aspx

note

  • 对比三个函数的2种情况,比如:ordinary reserve()以及reserve(),就可以看到allocator的用法;
  • allocatordestory()销毁的是已经被赋过值了的那些东西,和deallocate()是不一样的;
  • 指针哪里什么时候可以用-> * ,智能指针unique_ptr就可以;
  • RAII是一种编程技术,创造条件使得可以用构造函数和析构函数从而达到防止资源泄漏的目的;

你可能感兴趣的:([C++] Vector Changing Size allocator 、RAII 、 unique_ptr)