C++ Primer 5th notes (chap 13 copy control) Example 2 memory management

1. Goal

Implement a simplified version of the standard library vector class, which only supports string, we named it StrVec.

2. Design Ideas

2.1 The allocator manages the memory pool of each StrVec object, which is a contiguous memory (an array of types). Elements used to build storage inserts

push_back插入元素, 检查内存池是否够用?
 . 够用,在内存池的下—可用位置构造对象·
 . 不够用,重组空间: 
   	  . allocator获取全新的更大的内存池
      . 将已有元素拷贝至新的内存池
      . 释放旧的内存池
      . 在新内存池的下—可用位置构造新加入的元素

Insert picture description here

2.2 Four tool functions:

alloc_n_copy will allocate memory and copy the elements in a given range.
free will destroy the constructed element and release the memory.
chk_n_alloc guarantees that StrVec has space for at least one new element. If there is no space to add a new element, chk_n_alloc will call the reallocate class to allocate more memory.
reallocate allocates new memory for StrVec when the memory is used up.

3. StrVec.h

class StrVec {
    
    
public:
	// copy control members
    StrVec(): 
	  elements(nullptr), first_free(nullptr), cap(nullptr) {
    
     }

	StrVec(const StrVec&);            // copy constructor
	StrVec &operator=(const StrVec&); // copy assignment

#ifdef NOEXCEPT
	StrVec(StrVec&&) noexcept;            // move constructor
	StrVec &operator=(StrVec&&) noexcept; // move assignment
	~StrVec() noexcept;                   // destructor
#else
	StrVec(StrVec&&) throw();            // move constructor
	StrVec &operator=(StrVec&&) throw(); // move assignment
	~StrVec() throw();                   // destructor
#endif

#ifdef INIT_LIST
	// additional constructor
	StrVec(std::initializer_list<std::string>);
#else // define a constructor that takes pointers to a range of elements
	StrVec(const std::string*, const std::string*);
#endif

    void push_back(const std::string&);  // copy the element
    void push_back(std::string&&);       // move the element

	// add elements
    size_t size() const {
    
     return first_free - elements; }
    size_t capacity() const {
    
     return cap - elements; }

	// iterator interface
	std::string *begin() const {
    
     return elements; }
	std::string *end() const {
    
     return first_free; }
    
#ifdef INIT_LIST   // no real substitute for initializer_list in assignments
	// operator functions covered in chapter 14
	StrVec &operator=(std::initializer_list<std::string>);   
#endif

	std::string& operator[](std::size_t n) 
		{
    
     return elements[n]; }

	const std::string& operator[](std::size_t n) const 
		{
    
     return elements[n]; }
	
#ifdef VARIADICS    // no direct substitute for variadic functions
	// emplace member covered in chapter 16
	template <class... Args> void emplace_back(Args&&...);
#endif
private:
    static std::allocator<std::string> alloc; // allocates the elements

	// utility functions:
	//  used by members that add elements to the StrVec
	void chk_n_alloc() 
		{
    
     if (size() == capacity()) reallocate(); }
    // used by the copy constructor, assignment operator, and destructor
	std::pair<std::string*, std::string*> alloc_n_copy
	    (const std::string*, const std::string*);
	void free();             // destroy the elements and free the space
    void reallocate();       // get more space and copy the existing elements
    std::string *elements;   // pointer to the first element in the array
    std::string *first_free; // pointer to the first free element in the array
    std::string *cap;        // pointer to one past the end of the array
};


#include <algorithm>

inline
#ifdef NOEXCEPT
StrVec::~StrVec() noexcept {
    
     free(); }
#else
StrVec::~StrVec() throw() {
    
     free(); }
#endif

4. StrVec.cpp

inline
std::pair<std::string*, std::string*> 
StrVec::alloc_n_copy(const std::string *b, const std::string *e)
{
    
    
	// allocate space to hold as many elements as are in the range
	auto data = alloc.allocate(e - b); 

	// initialize and return a pair constructed from data and
	// the value returned by uninitialized_copy
#ifdef LIST_INIT
	return {
    
    data, uninitialized_copy(b, e, data)};
#else
	return make_pair(data, uninitialized_copy(b, e, data));
#endif
}

inline
#ifdef NOEXCEPT
StrVec::StrVec(StrVec &&s) noexcept  // move won't throw any exceptions
#else
StrVec::StrVec(StrVec &&s) throw()   // move won't throw any exceptions
#endif
  // member initializers take over the resources in s
  : elements(s.elements), first_free(s.first_free), cap(s.cap)
{
    
    
	// leave s in a state in which it is safe to run the destructor
	s.elements = s.first_free = s.cap = nullptr;
}

inline StrVec::StrVec(const StrVec &s)
{
    
    
	// call alloc_n_copy to allocate exactly as many elements as in s
	auto newdata = alloc_n_copy(s.begin(), s.end());
	elements = newdata.first; 
	first_free = cap = newdata.second;
}

inline void StrVec::free()
{
    
    
    // may not pass deallocate a 0 pointer; if elements is 0, there's no work to do
	if (elements) {
    
    
    	// destroy the old elements in reverse order
		for (auto p = first_free; p != elements; /* empty */)
			alloc.destroy(--p);  
		alloc.deallocate(elements, cap - elements);
	}
}
	
#ifdef INIT_LIST
inline StrVec &StrVec::operator=(std::initializer_list<std::string> il)
{
    
    
	// alloc_n_copy allocates space and copies elements from the given range
	auto data = alloc_n_copy(il.begin(), il.end());
	free();   // destroy the elements in this object and free the space
	elements = data.first; // update data members to point to the new space
	first_free = cap = data.second;
	return *this;
}
#endif 

inline
#ifdef NOEXCEPT
StrVec &StrVec::operator=(StrVec &&rhs) noexcept
#else
StrVec &StrVec::operator=(StrVec &&rhs) throw()
#endif
{
    
    
	// direct test for self-assignment
	if (this != &rhs) {
    
    
		free();                   // free existing elements 
		elements = rhs.elements;  // take over resources from rhs
		first_free = rhs.first_free;
		cap = rhs.cap;
		// leave rhs in a destructible state
		rhs.elements = rhs.first_free = rhs.cap = nullptr;
	}
	return *this;
}

inline StrVec &StrVec::operator=(const StrVec &rhs)
{
    
    
	// call alloc_n_copy to allocate exactly as many elements as in rhs
	auto data = alloc_n_copy(rhs.begin(), rhs.end());
	free();
	elements = data.first;
	first_free = cap = data.second;
	return *this;
}

inline void StrVec::reallocate()
{
    
    
    // we'll allocate space for twice as many elements as the current size
    auto newcapacity = size() ? 2 * size() : 1;

  // allocate new memory
	auto newdata = alloc.allocate(newcapacity);

 // move the data from the old memory to the new
	auto dest = newdata;  // points to the next free position in the new array
       auto elem = elements; // points to the next element in the old array
	for (size_t i = 0; i != size(); ++i)
		alloc.construct(dest++, std::move(*elem++));

	free();  // free the old space once we've moved the elements

    // update our data structure to point to the new elements
    elements = newdata;
    first_free = dest;
    cap = elements + newcapacity;
}

#ifdef INIT_LIST
inline StrVec::StrVec(std::initializer_list<std::string> il)
{
    
    
	// call alloc_n_copy to allocate exactly as many elements as in il
	auto newdata = alloc_n_copy(il.begin(), il.end());
	elements = newdata.first;
	first_free = cap = newdata.second;
}
#else
inline StrVec::StrVec(const std::string *b, const std::string* e)
{
    
    
	// call alloc_n_copy to allocate exactly as many elements as in the range
	auto newdata = alloc_n_copy(b, e);
	elements = newdata.first;
	first_free = cap = newdata.second;
}
#endif

inline void StrVec::push_back(const std::string& s)
{
    
    
    chk_n_alloc(); // ensure that there is room for another element
    // construct a copy of s in the element to which first_free points
    alloc.construct(first_free++, s);  
}

inline void StrVec::push_back(std::string &&s) 
{
    
    
    chk_n_alloc(); // reallocates the StrVec if necessary
	alloc.construct(first_free++, std::move(s));
}
 
#ifdef VARIADICS    // no direct substitute for variadic functions
// emplace member covered in chapter 16
template <class... Args>
inline void StrVec::emplace_back(Args&&... args)
{
    
    
    chk_n_alloc(); // reallocates the StrVec if necessary
	alloc.construct(first_free++, std::forward<Args>(args)...);
}
#endif

#endif

【reference】

[1] Code StrVec.h

Guess you like

Origin blog.csdn.net/thefist11cc/article/details/113914384