C++中allocator類使用示例
動態(tài)內(nèi)存管理
之前我們講述過動態(tài)內(nèi)存的開辟,可以通過new, malloc,以及alloc等方式,本文通過介紹alloc方式,構(gòu)造一個StrVec類,這個類的功能類似于一個vector,實現(xiàn)字符串的管理,其中包含push一個字符串,動態(tài)擴(kuò)容,析構(gòu),回收內(nèi)存等操作。
StrVec類實現(xiàn)細(xì)節(jié)
StrVec類實現(xiàn)如下
class StrVec
{
public:
//無參構(gòu)造函數(shù)
StrVec() : elements(nullptr), first_free(nullptr),
cap(nullptr) {}
//拷貝構(gòu)造函數(shù)
StrVec(const StrVec &);
//拷貝賦值運算符
StrVec &operator=(const StrVec &);
//析構(gòu)函數(shù)
~StrVec();
//拷貝元素
void push_back(const std::string &);
//返回元素個數(shù)
size_t size() const { return first_free - elements; }
//返回總?cè)萘?
size_t capacity() const { return cap - elements; }
//返回首元素地址
std::string *begin() const
{
return elements;
}
//返回第一個空閑元素地址
//也是最后一個有效元素的下一個位置
std::string *end() const
{
return first_free;
}
private:
//判斷容量不足開辟新空間
void chk_n_alloc()
{
if (size() == capacity())
reallocate();
}
//重新開辟空間
void reallocate();
// copy指定范圍的元素到新的內(nèi)存中
std::pair<std::string *, std::string *> alloc_n_copy(
const std::string *, const std::string *);
//釋放空間
void free();
//數(shù)組首元素的指針
std::string *elements;
//指向數(shù)組第一個空閑元素的指針
std::string *first_free;
//指向數(shù)組尾后位置的指針
std::string *cap;
//構(gòu)造string類型allocator靜態(tài)成員
static std::allocator<std::string> alloc;
};
1 elements成員,該成員指向StrVec內(nèi)部數(shù)組空間的第一個元素
2 first_free成員指向第一個空閑元素,也就是有效元素的下一個元素,該元素開辟空間但未構(gòu)造。
3 cap 指向最后一個元素的下一個位置。
4 alloc為靜態(tài)成員,主要負(fù)責(zé)string類型數(shù)組的開辟工作。
5 無參構(gòu)造函數(shù)將三個指針初始化為空,并且默認(rèn)夠早了alloc。
6 alloc_n_copy私有函數(shù)的功能是將一段區(qū)域的數(shù)據(jù)copy到新的空間,
并且返回新開辟的空間地址以及第一個空閑元素的地址(第一個未構(gòu)造元素的地址)。
7 chk_n_alloc私有函數(shù)檢測數(shù)組大小是否達(dá)到容量,如果達(dá)到則調(diào)用reallocate重新開辟空間。
8 reallocate重新開辟空間
9 capacity返回總?cè)萘?br />10 size返回元素個數(shù)
11 push_back 將元素放入開辟的類似于數(shù)組的連續(xù)空間中。
12 begin返回首元素地址
13 end返回第一個空閑元素地址,也是最后一個有效元素的下一個位置
無論我們實現(xiàn)push操作還是拷貝構(gòu)造操作,都要實現(xiàn)realloc,當(dāng)空間不足時要開辟空間將舊數(shù)據(jù)移動到新的數(shù)據(jù)
//重新開辟空間
void StrVec::reallocate()
{
string *newdata = nullptr;
//數(shù)組為空的情況
if (elements == nullptr || cap == nullptr || first_free == nullptr)
{
newdata = alloc.allocate(1);
// elements和first_free都指向首元素
elements = newdata;
first_free = newdata;
// cap指向數(shù)組尾元素的下一個位置。
cap = newdata + 1;
return;
}
//不為空則擴(kuò)充兩倍空間
newdata = alloc.allocate(size() * 2);
//新內(nèi)存空閑位置
auto dest = newdata;
//舊內(nèi)存有效位置
auto src = elements;
//通過移動操作將舊數(shù)據(jù)放到新內(nèi)存中
for (size_t i = 0; i != size(); ++i)
{
alloc.construct(dest++, std::move(*src++));
}
//移動后舊內(nèi)存數(shù)據(jù)無效,一定要刪除
free();
//更新數(shù)據(jù)位置
elements = newdata;
//更新第一個空閑位置
first_free = dest;
//更新容量
cap = elements + size() * 2;
}
reallocate函數(shù)內(nèi)部判斷是否為剛初始化指針卻沒開辟空間的空數(shù)組,如果是則開辟1個大小的空間。
否則則開辟原有空間的兩倍,將舊數(shù)據(jù)移動到新空間,采用了std::move操作,這么做減少拷貝造成的性能開銷。
move之后原數(shù)據(jù)就無效了,所以要調(diào)用私有函數(shù)free()進(jìn)行釋放。我們實現(xiàn)該free操作
//釋放操作
void StrVec::free()
{
//判斷elements是否為空
if (elements == nullptr)
{
return;
}
auto dest = elements;
//要先遍歷析構(gòu)每一個對象
for (size_t i = 0; i < size(); i++)
{
// destroy會調(diào)用每一個元素的析構(gòu)函數(shù)
alloc.destroy(dest++);
}
//再整體回收內(nèi)存
alloc.deallocate(elements, cap - elements);
}
先通過遍歷destroy銷毀內(nèi)存,從而調(diào)用string的析構(gòu)函數(shù),最后在deallocate回收內(nèi)存。
// copy指定范圍的元素到新的內(nèi)存中,返回新元素的地址和第一個空閑元素地址的pair
std::pair<std::string *, std::string *> StrVec::alloc_n_copy(
const std::string *b, const std::string *e)
{
auto newdata = alloc.allocate(e - b);
//將原數(shù)據(jù)用來初始化新空間
auto first_free = uninitialized_copy(b, e, newdata);
return {newdata, first_free};
}
這樣利用alloc_n_copy,我們就可以實現(xiàn)拷貝構(gòu)造和拷貝賦值了
//拷貝構(gòu)造函數(shù)
StrVec::StrVec(const StrVec &strtmp)
{
//將形參數(shù)據(jù)拷貝給自己
auto rsp = alloc_n_copy(strtmp.begin(), strtmp.end());
//更新elements, cap,first_free
elements = rsp.first;
first_free = rsp.second;
cap = rsp.second;
}
但是拷貝賦值要注意一點,就是自賦值的情況,所以我們提前判斷是否為自賦值,如不是則進(jìn)行和拷貝構(gòu)造相同的操作
//拷貝賦值運算符
StrVec &StrVec::operator=(const StrVec &strtmp)
{
//防止自賦值
if (this == &strtmp)
{
return *this;
}
//將形參數(shù)據(jù)拷貝給自己
auto rsp = alloc_n_copy(strtmp.begin(), strtmp.end());
//更新elements, cap,first_free
elements = rsp.first;
first_free = rsp.second;
cap = rsp.second;
}
我們可以利用free實現(xiàn)析構(gòu)函數(shù)
//析構(gòu)
StrVec::~StrVec()
{
free();
}
接下來我們實現(xiàn)push_back,將指定字符串添加到數(shù)組空間,以及拋出元素
//添加元素
void StrVec::push_back(const std::string &s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
}
//拋出元素
void StrVec::pop_back(std::string &s)
{
if (first_free == nullptr)
{
return;
}
if (size() == 1)
{
s = *elements;
alloc.destroy(elements);
first_free = nullptr;
elements = nullptr;
return;
}
s = *(--first_free);
alloc.destroy(first_free);
}
接下來實現(xiàn)測試函數(shù),測試上述操作
void test_strvec()
{
auto str1 = StrVec();
str1.push_back("hello zack");
StrVec str2(str1);
str2.push_back("hello rolin");
StrVec str3 = str1;
string strtmp;
str3.pop_back(strtmp);
}
在主函數(shù)調(diào)用上面test_strvec,運行穩(wěn)定。
總結(jié)
本文通過allocator實現(xiàn)了一個類似于vector的類,管理string變量。演示了拷貝構(gòu)造,拷貝賦值要注意的事項,同時演示了如何手動開辟內(nèi)存并管理內(nèi)存空間。
到此這篇關(guān)于C++中allocator類使用示例的文章就介紹到這了,更多相關(guān)C++ allocator類內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c++動態(tài)內(nèi)存管理與智能指針的相關(guān)知識點
為了更容易同時也更安全地使用動態(tài)內(nèi)存,新的標(biāo)準(zhǔn)庫提供了兩種智能指針(smart pointer)類型來管理對象,下面這篇文章主要給大家介紹了關(guān)于c++動態(tài)內(nèi)存管理與智能指針的相關(guān)知識點,需要的朋友可以參考下2022-03-03
C語言數(shù)據(jù)的存儲超詳細(xì)講解中篇練習(xí)
使用編程語言進(jìn)行編程時,需要用到各種變量來存儲各種信息。變量保留的是它所存儲的值的內(nèi)存位置。這意味著,當(dāng)您創(chuàng)建一個變量時,就會在內(nèi)存中保留一些空間。您可能需要存儲各種數(shù)據(jù)類型的信息,操作系統(tǒng)會根據(jù)變量的數(shù)據(jù)類型,來分配內(nèi)存和決定在保留內(nèi)存中存儲什么2022-04-04

