C++Primer第十三章 动态内存管理习题
编写自己版本的StrVec(string版本的vector)
#pragma once
#ifndef STRVEC_H
#define STRVEC_H
#include<string>
#include<memory>
#include<utility>
#include<initializer_list>
#include<algorithm>
using std::string;
using std::allocator;
using std::pair;
class StrVec {
public:
//默认构造函数
StrVec() :elements(nullptr), first_free(nullptr), cap(nullptr) {};
StrVec(const StrVec&);
//通过列表初始化容器来构造
StrVec(std::initializer_list<string>);
StrVec& operator=(const StrVec&);
~StrVec();
//重载中括号,一边快速索引vector内的元素
string& operator[](size_t)const;
void push_back(const string&);
size_t size()const { return first_free - elements; }//对象个数
size_t capacity()const { return cap - elements; }//内存空间大小(内存空间大小>对象个数)
string* begin()const { return elements; }
string* end()const { return first_free; }
void reseve(size_t);//用来按需扩充内存空间
void resize(size_t, const string& s);//调整元素个数,少则删,多则添,不能减少原本就存在的内存空间,只能增加或者不变
private:
//因为所有对象公用一个内存分配器,所以需要一个静态的内存分配器
static allocator<string> alloc;
//传入两根内存指针,返回新创建的两根内存指针(拷贝赋值操作符使用)
pair<string*, string*> alloc_n_copy(const string*, const string*);//分配指定内存后拷贝,目的在于拷贝(创建)出来一个新的内存空间副本,主要在拷贝构造函数/拷贝赋值操作符内使用
//传入需要更新的内存大小(resize,
void alloc_n_move(size_t);
void chk_n_alloc();//检查内存空间是否满足插入需求,如果不满足插入需求,那么我们需要调用reallocate()重新分配一个新的空间,并且将原来的元素移动到新的内存空间
//使用移动构造
void reallocate();//扩充内存并且移动原来内存空间的元素
void free();
//成员属性
string* elements;//第一个元素
string* first_free;//指向最后一个实际元素之后的位置
string* cap;//指向分配内存末尾之后的位置
};
//拷贝构造函数
StrVec::StrVec(const StrVec& v) {
auto new_data = alloc_n_copy(v.begin(), v.end());
elements = new_data.first;
first_free = new_data.second;
}
StrVec::StrVec(std::initializer_list<string> ls)
{
auto new_data = alloc_n_copy(ls.begin(), ls.end());
elements = new_data.first;
first_free= cap = new_data.second;
}
//拷贝赋值函数
//特点:组合了拷贝构造和析构函数的功能
StrVec& StrVec::operator=(const StrVec& v) {
auto new_data = alloc_n_copy(v.begin(), v.end());
free();
elements = new_data.first;
first_free = cap = new_data.second;
return *this;
}
void StrVec::free() {
//不能传递给deallocate一个空指针,因为如果elements=0,那么函数什么也不做
if (elements) {//如果这段内存是存在的
//使用for循环destroy
//for (auto i = first_free; i != elements;) {
// //因为first_free一开始指向最后一个元素的下一个位置,所以是前置递减运算符
// alloc.destroy(--i);
//}
//使用for_each
for_each(elements, first_free, [this](string& rhs) { alloc.destroy(&rhs); });//捕获this指针
alloc.deallocate(elements, cap - elements);
}
}
pair<string*, string*>
StrVec::alloc_n_copy(const string* b, const string* e) {
string* data = alloc.allocate(e - b);//创建出(e-b)那么大的内存空间
//uninitialized_copy与uninitialized_fill的区别?
return { data,uninitialized_copy(b,e,data) };
}
void StrVec::alloc_n_move(size_t new_cap)//分配并且移动
{
auto new_data = alloc.allocate(new_cap);
auto elem = elements;
auto dest = new_data;
//移动原本存在的元素
for (size_t i = 0; i != size(); i++) {
alloc.construct(dest++, std::move(*elem++));
}
elements = new_data;
first_free = dest;
cap = elements + new_cap;
}
StrVec::~StrVec()
{
free();
}
string& StrVec::operator[](size_t i) const
{
return *(elements + i);
}
void StrVec::push_back(const string& str) {
chk_n_alloc();//检查内存是否符合要求,不符合要求就重新分配(重新分配内存加移动元素)
alloc.construct(first_free++, str);//在内存空间上构造str这个对象
}
void StrVec::chk_n_alloc() {
if (size() == capacity()) { reallocate(); }
}
void StrVec::reallocate()
{
auto new_capacity = size() ? 2 * size() : 1;//计算扩充后的内存大小,有两种情况,一种是原来就没有分配内存空间,另一种是分配了内存空间但是不够用
alloc_n_move(new_capacity);
}
void StrVec::reseve(size_t new_cap)
{
if (new_cap < capacity())//若重新分配的容量比原来还小,那么不允许分配,因为reseve是不容许改变容器内元素的数量的
return;
alloc_n_move(new_cap);//重新分配空间并且move
}
//resize可以扩充原本容器的空间,但无法减少容器预留的空间,resize本质是改变元素的数量
void StrVec::resize(size_t new_size, const string& s = "")
{
if (new_size > size()) {
if (new_size > capacity())reseve(new_size * 2);
for (size_t i = size(); i < new_size; i++) {
alloc.construct(first_free++, s);
}
}
else if (new_size < size()) {
for (size_t i = 0; i < new_size; i++) {
alloc.destroy(--first_free);//这里注意是前置递减
}
}
}
allocator<string> StrVec::alloc;
#endif
编写简易版的string
#pragma once
#ifndef STRING_H
#define STRING_H
#include<memory>
#include<algorithm>
#include<iostream>
using namespace std;
class String {
friend ostream& operator<<(ostream&,const String&);
public:
String();
String(const char*);
String(const String&);
String& operator=(const String&);
~String() { free(); }
char* begin()const{ return elements; }
char* end()const { return first_free; }
private:
pair<char*, char*> alloc_n_copy(const char*,const char*);
void free() {
if (elements) {
for_each(elements, first_free, [this](char& rhs) {alloc.destroy(&rhs); });
alloc.deallocate(elements,first_free-elements);
}
};
//static?
static allocator<char> alloc;
char* elements;
char* first_free;
};
String::String(const char* s) {
auto* s1 = const_cast<char*>(s);
while (*s1)
s1++;
auto p=alloc_n_copy(s, s1);
elements = p.first;
first_free = p.second;
}
String::String(const String& s) {
auto p = alloc_n_copy(s.begin(), s.end());
elements = p.first;
first_free = p.second;
}
String& String::operator=(const String& s) {
//防止自赋值
auto p = alloc_n_copy(s.begin(), s.end());
free();
elements = p.first;
first_free = p.second;
return *this;
}
ostream& operator<<(ostream& os, const String& s) {
char* ch = s.begin();
while (ch != s.end()) {
os << *ch;
ch++;
}
return os;
}
pair<char*, char*> String::alloc_n_copy(const char* b, const char* e) {
auto s1 = alloc.allocate(e-b);
auto s2=uninitialized_copy(b,e, s1);
return {s1,s2};
}
allocator<char> String::alloc;
#endif
测试代码
#include"StrVec.h"
#include"String.h"
#include<string>
#include<iostream>
using namespace std;
int main(int argc, char** argv) {
StrVec v1;
v1.push_back("Harris");
v1.push_back("Jack");
v1.push_back("rommi");
v1.push_back("Tim");
v1.push_back("Tom");
v1.push_back("mike");
测试resize
//v1.resize(7, "new_data");
//v1.resize(8,"new_data");
//v1.resize(9, "new_data");
测试reseve
//v1.reseve(100);
//测试operator=
StrVec v2;
v2.resize(10);
v2 = v1;
cout << "v2.cap=" << v2.capacity() << " v2.size=" << v2.size() << endl;
for (int i = 0; i < v2.size(); i++) {
cout << v2[i] << endl;
}
测试用初始值列表初始化vec
//StrVec v3 = { "Hello","World" };
//cout << "v3.cap=" << v3.capacity() << " v3.size=" << v3.size() << endl;
//for (int i = 0; i < v3.size(); i++) {
// cout << v3[i] << endl;
//}
cout << "v1.cap=" << v1.capacity() << " v1.size=" << v1.size() << endl;
for (int i = 0; i < v1.size(); i++) {
cout << v1[i] << endl;
}
//测试String
String s1("Hello!");
cout << s1<<endl;
String s2(s1);
s1 = s2;
cout << s2<<endl;
String s3("world!");
s1 = s3;
cout << s1;
return 1;
}
[进阶]模板vector(兼容任意类型)
#pragma once
#ifndef VEC_H
#define VEC_H
#include<memory>
#include<utility>
#include<algorithm>
#include<iostream>
using std::allocator;
using std::pair;
using std::initializer_list;
using std::cout;
using std::endl;
template<typename T> class vec;
template<typename T>
class vec {
public:
vec() :elements(nullptr), first_free(nullptr), cap(nullptr){}
vec(const vec&);
vec(vec&&)noexcept;
vec(initializer_list<T>);
vec& operator=(const vec&);
vec& operator=(vec&&)noexcept;
~vec();
T& operator[](size_t)const;
void push_back(const T&);//拷贝push_back
void push_back(T&&)noexcept;//移动push_back
size_t size()const { return first_free - elements; }//返回元素个数
size_t capacity()const { return cap - elements; }//返回容量大小
T* begin()const { return elements; }//返回头部指针
T* end()const { return first_free; }//返回尾部指针
void reseve(size_t);//扩充容量
void resize(size_t,const T&);//更改元素数量
private:
static allocator<T> alloc;//内存分配器
pair<T*, T*>alloc_n_copy(const T*, const T*);//拷贝元素的方式分配内存
void alloc_n_move(size_t);//移动元素的方式分配内存
void chk_n_alloc();//判断是否需要重新分配内存
void reallocate();//分配一个新的capacity
void free();
T* elements;
T* first_free;
T* cap;//内存指针
};
template<typename T>
vec<T>::vec(const vec& v) {
auto new_data=alloc_n_copy(v.begin(), v.end());
elements = new_data.first;
cap = new_data.second;
}
template<typename T>
vec<T>::vec(vec&& v)noexcept :elements(v.elements), first_free(v.first_free), cap(v.cap) {
v.elements = v.first_free = v.cap = nullptr;
}
template<typename T>
vec<T>::vec(initializer_list<T> ls) {
auto new_data = alloc_n_copy(ls.begin(), ls.end());
elements = new_data.first;
first_free = cap = new_data.second;
}
template<typename T>
vec<T>& vec<T>::operator=(const vec& v) {
auto new_data = alloc_n_copy(v.begin(), v.end());
free();
elements = new_data.first;
first_free=cap = new_data.second;
return *this;
}
template<typename T>
vec<T>& vec<T>::operator=(vec&& v)noexcept{
if (this!=&v) {
free();
elements = v.elements;
first_free = v.first_free;
cap = v.cap;
v.elements = v.first_free = v.cap = nullptr;
}
return *this;
}
template<typename T>
vec<T>::~vec() { free(); };
template<typename T>
T& vec<T>::operator[](size_t index)const{
return *(elements + index);
}
template<typename T>
void vec<T>::chk_n_alloc() {
if (size() == capacity()) { reallocate(); }
}
template<typename T>
void vec<T>::reallocate() {
size_t new_capacity = size() ? size() * 2 : 1;
alloc_n_move(new_capacity);
}
template<typename T>
pair<T*, T*> vec<T>::alloc_n_copy(const T* b, const T* e) {
T* data = alloc.allocate(e - b);//分配一段大小为(e-b)的内存空间,返回这段内存空间的开始地址
return { data,std::uninitialized_copy(b,e,data) };
}
template<typename T>
void vec<T>::alloc_n_move(size_t new_cap) {
T* new_data = alloc.allocate(new_cap);
T* dest=new_data;
T* elem=elements;
for (size_t i = 0; i != size(); i++) {
alloc.construct(dest++, std::move(*elem++));
}
//更新内存地址
elements = new_data;
first_free = dest;
cap = elements + new_cap;
}
template<typename T>
void vec<T>::free() {
if (elements) {
从后往前解除对象构造
//for (auto i = first_free; i != elements) {
// alloc.destory(--i);
//}
//for_each
std::for_each(begin(), end(), [this](T& rhs) {alloc.destroy(&rhs); });
}
alloc.deallocate(elements, capacity());//销毁内存空间
}
template<typename T>
void vec<T>::push_back(const T& obj) {
chk_n_alloc();
alloc.construct(first_free++, obj);
}
template<typename T>
void vec<T>::push_back(T&& element)noexcept {
chk_n_alloc();
alloc.construct(first_free++, std::move(element));
}
template<typename T>
void vec<T>::reseve(size_t new_cap) {
if (new_cap < capacity())
return;
alloc_n_move(new_cap);
}
template<typename T>
void vec<T>::resize(size_t new_sz,const T& obj) {
if (new_sz > size()) {
if (new_sz > capacity()) {
alloc_n_move(new_sz);
}
for (size_t i = size(); i < new_sz; i++) {
alloc.construct(first_free++, obj);
}
}
else if(new_sz<size()){
while(first_free!=elements+new_sz) {
alloc.destroy(--first_free);
}
}
}
template<typename T> allocator<T> vec<T>::alloc;
#endif
还可以添加一个emplace_back成员函数
实现原理:可变参数模板
+forwar机制
template<typename T>
template<typename ...Args>
inline
void vec<T>::emplace_back(Args&&... args) {
chk_n_alloc();
//同时扩展Args参数包与args参数包
alloc.construct(first_free++, std::forward<Args>(args)...);
}