前情回顾: allocator内存管理类
allocator内存管理器
某些类需要在运行时分配可变大小的内存空间,一般来说我们使用容器如vector来管理我们的数据,但是对于某些类,在有些时候我们需要自己进行内存的分配。这些类必须定义自己的拷贝控制成员来管理所分配的内存。
我们将实现一个vector,这个vector保存的是sring类型的数据,而且使用我们自己创建的内存分配的方式。我们的这个类叫做 vecStr
基本属性
我们在自己创建的vecStr中需要有三个属性,分别是:
- element: 指向分配的内存的起始位置。
- first_free:指向已经构造完成的对象的下一个位置,即未构造的内存的起始位置。
- cap:总的内存空间的尾后位置。
如图所示:
类的设计
基本构造函数:
strVec();
strVec(std::initializer_list<std::string> initList);
~strVec();
strVec(const strVec& other);
strVec& operator=(const strVec& other);
以下几个具有关键功能的函数:
- alloc_n_copy:分配内存,并且拷贝给定范围的元素到一个新的内存中。
std::pair<std::string*, std::string*> alloc_n_copy(const std::string* beg, const std::string* end);
- reallocate: 当内存不够时,重新分配一块新的内存,并且拷贝原始内容,释放旧的内存
void reallocate();
void free();
- check_n_alloc:检查当前内存空间是否足够,不够的话调用reallocate重新分配一块内存。
void check_n_alloc();
其他功能性函数:
size_t capacity()const { return cap - element; }
size_t size()const { return first_free - element; }
void push_back(const std::string& str);
关键功能的实现
- alloc_n_copy:接受一个开始位置和结束的位置的指针,开辟一块新的内存空间,并且把开始位置到结束位置中的内容拷贝到这块新的内存空间,并且返回这块内存空间的初始位置和尾后位置。
我们使用pair来保存这两个位置。
std::pair<std::string*, std::string*> strVec::alloc_n_copy(const std::string* beg, const std::string* end)
{
auto p = allocStr.allocate(end - beg);
return { p,std::uninitialized_copy(beg, end, p) };
}
使用uninitialized_copy来拷贝beg到end的内存空间的内容到新的内存空间p中。
- reallocate:旧内存空间不够时,我们重新开辟一块内存,并且把原始内容拷贝到新内存中,注意我们的新内存一般是原始内存的两倍大小。
- 使用move的进行移动构造,从而避免拷贝构造(拷贝后销毁)的繁琐操作,直接进行指针所有权的转移即可,这就是移动构造函数。
- 使用construct构造对象。
- 注意属性的更新,element first_free cap此时都指向了新的内存空间的对应位置。
void strVec::reallocate()
{
auto NewSpace = (size() == 0) ? 1 : 2 * size();
auto pNew = allocStr.allocate(NewSpace);
auto dest = pNew;
auto old = element;
for (size_t i = 0; i != size(); i++)
{
allocStr.construct(dest++, std::move(*old++));
}
free();
element = pNew;
first_free = dest;
cap = element + NewSpace;
}
- free:释放旧的内存空间,我们使用三种方法来释放旧的内存空间,for_each函数式,正序销毁和逆序销毁,注意,销毁只是调用了他们的析构函数,我们一定最后使用deallocate来彻底释放这块内存。
void strVec::free()
{
if (element)
{
#if 0
std::for_each(begin(), end(), [&](std::string& str)
{
allocStr.destroy(&str);
});
#elif 0
#else
for (auto elebeg = element; elebeg != first_free;)
{
allocStr.destroy(elebeg++);
}
#endif
allocStr.deallocate(element, cap - element);
}
}
完整的内存管理器
#pragma once
#include <iostream>
#include <vector>
#include <memory>
#include <vld.h>
#include <algorithm>
class strVec
{
public:
strVec();
strVec(std::initializer_list<std::string> initList);
~strVec();
strVec(const strVec& other);
strVec& operator=(const strVec& other);
size_t capacity()const { return cap - element; }
size_t size()const { return first_free - element; }
void push_back(const std::string& str);
void reserve(const int& n);
void resize(const int& n, const std::string& str = "None");
public:
std::string* begin()const { return element; }
std::string* end()const { return first_free; }
private:
std::pair<std::string*, std::string*> alloc_n_copy(const std::string* beg, const std::string* end);
void free();
void check_n_alloc();
void reallocate();
void reallocate(int n);
private:
std::allocator<std::string> allocStr;
std::string* element;
std::string* first_free;
std::string* cap;
};
strVec::strVec()
:element(nullptr), first_free(nullptr), cap(nullptr)
{
}
strVec::strVec(std::initializer_list<std::string> initList)
{
int n = initList.size();
auto p = allocStr.allocate(n);
element = first_free = p;
cap = p + n;
for (auto& xStr : initList)
{
allocStr.construct(first_free++, xStr);
}
}
strVec::~strVec()
{
free();
}
strVec::strVec(const strVec& other)
{
auto pPair = alloc_n_copy(other.element, other.first_free);
element = pPair.first;
first_free = pPair.second;
cap = pPair.second;
}
strVec& strVec::operator=(const strVec& other)
{
auto pPair = alloc_n_copy(other.element, other.first_free);
free();
element = pPair.first;
first_free = cap = pPair.second;
return *this;
}
void strVec::push_back(const std::string& str)
{
check_n_alloc();
allocStr.construct(first_free++, str);
}
void strVec::reserve(const int& n)
{
if (n > capacity())
{
reallocate(n);
}
}
void strVec::resize(const int& n, const std::string& str)
{
if (n < capacity())
{
int m = capacity() - n;
for (int i = 0; i < m; i++)
{
allocStr.destroy(--first_free);
}
cap = first_free;
}
else
{
reallocate(n);
while (first_free != cap)
{
allocStr.construct(first_free++, str);
}
}
}
std::pair<std::string*, std::string*> strVec::alloc_n_copy(const std::string* beg, const std::string* end)
{
auto p = allocStr.allocate(end - beg);
return { p,std::uninitialized_copy(beg, end, p) };
}
void strVec::free()
{
if (element)
{
#if 0
std::for_each(begin(), end(), [&](std::string& str)
{
allocStr.destroy(&str);
});
#elif 0
#else
for (auto elebeg = element; elebeg != first_free;)
{
allocStr.destroy(elebeg++);
}
#endif
allocStr.deallocate(element, cap - element);
}
}
void strVec::check_n_alloc()
{
if (size() == capacity())
{
reallocate();
}
}
void strVec::reallocate()
{
auto NewSpace = (size() == 0) ? 1 : 2 * size();
auto pNew = allocStr.allocate(NewSpace);
auto dest = pNew;
auto old = element;
for (size_t i = 0; i != size(); i++)
{
allocStr.construct(dest++, std::move(*old++));
}
free();
element = pNew;
first_free = dest;
cap = element + NewSpace;
}
void strVec::reallocate(int n)
{
auto NewSpace = n;
auto pNew = allocStr.allocate(NewSpace);
auto dest = pNew;
auto old = element;
for (size_t i = 0; i != size(); i++)
{
allocStr.construct(dest++, std::move(*old++));
}
free();
element = pNew;
first_free = dest;
cap = element + NewSpace;
}
内存管理器的测试:设计自定义的String类。
class String
{
public:
String();
String(const char* str);
String(const String& other);
String& operator=(const String& other);
~String();
size_t size() const { return end - element; }
public:
private:
void free();
std::pair<char*, char*> alloc_n_copy(const char* beg,const char* end);
char* element;
char* end;
std::allocator<char> alloCh;
};
int main()
{
String s{"woaini"};
String s1{ s };
String s2;
s2 = s1;
return 0;
}
String::String()
:element(nullptr),end(nullptr)
{
}
String::String(const char* str)
{
int len = strlen(str);
auto pStr = alloc_n_copy(str, str + len);
element = pStr.first;
end = pStr.second;
}
String::String(const String& other)
{
auto pNew = alloc_n_copy(other.element, other.end);
element = pNew.first;
end = pNew.second;
}
String& String::operator=(const String& other)
{
auto pNew = alloc_n_copy(other.element, other.end);
free();
element = pNew.first;
end = pNew.second;
return *this;
}
String::~String()
{
free();
}
void String::free()
{
if (element)
{
std::for_each(element, end, [&](char& str)
{
alloCh.destroy(&str);
});
alloCh.deallocate(element, end - element);
}
}
std::pair<char*, char*> String::alloc_n_copy(const char* beg, const char* end)
{
auto p = alloCh.allocate(end - beg);
return { p,std::uninitialized_copy(beg,end,p) };
}
|