C++Primer第十三章13.5(编写StrVec类+简易的string类+[进阶]vec模板类)

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)...);
}

参考博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值