C++11提供STL的emplace方法剖析二

可变参模板

可变参表示参数的类型和数量都可变

void show(int a, string b) {
	cout << a << endl;
	cout << b << endl;
}

template<typename... Types>
void fun(Types... args) {
	show(args...);
}

int main() {
	fun(1, "hello");
	return 0;
}

使用空间配置器和可变参模板的vector

#include<iostream>
#include<vector>
#include<unordered_map>

using namespace std;

const int INIT_SIZE = 10;

class Test{
public:
	Test(int a)
		: a_(a)
		, b_("")
	{ 
		cout << "Test(int)" << endl; 
	}
	Test(int a, string b)
		: a_(a)
		, b_(b)
	{ 
		cout << "Test(int,string)" << endl; 
	}
	Test(const Test& a) { cout << "Test(const Test&)" << endl; }
	Test(const Test&& a) { cout << "Test(const Test&&)" << endl; }
	~Test() { cout << "~Test()" << endl; }

	int a_;
	string b_;
};

template<typename T>
class Allocator {
public:
	// 开辟size字节
	T* allocate(size_t size) {
		return (T*)malloc(size);
	}

	void deallocate(void* p) {
		free(p);
	}

	template<typename... Types>
	void construct(T* p, Types&&... args) {
		// 当给emplace传入Test对象时,Types就是Test&或Test&&,T就是Test
		// 当给emplace传入Test构造所需参数时,Types就是int string,可变参表示参数的类型和数量都可变
		new (p) T(std::forward<Types>(args)...);
	}

	void destroy(T* p) {
		p->~T();
	}
};


template<typename T, typename Alloc = Allocator<T>>
class Vector {
public:
	Vector(int size = INIT_SIZE, const Alloc& alloc = Allocator<T>())
		: allocator_(alloc)
	{
		// first_ = new T[size];
		first_ = allocator_.allocate(size * sizeof(T));
		last_ = first_;
		end_ = first_ + size;
	}

	~Vector() {
		// delete[] first_;

		// 析构时,只析构容器中有效元素[first_, last - 1]
		for (T* p = first_; p != last_; p++) {
			allocator_.destroy(p);
		}
		// 释放整个容器空间
		allocator_.deallocate(first_);
		last_ = first_ = end_ = nullptr;
	}

	Vector(const Vector<T>& src) {
		// 深拷贝
		// 开辟空间
		int size = src.end_ - src.first_;
		// first_ = new T[size];
		first_ = allocator_.allocate(sizeof(T) * size);

		// 拷贝有效元素
		int len = src.last_ - src.first_;
		for (int i = 0; i < len; i++) {
			// first_[i] = src.first_[i];
			allocator_.construct(first_ + i, src.first_[i]);
		}
		last_ = first_ + len;
		end_ = first_ + size;
	}

	Vector<T>& operator=(const Vector<T>& src) {
		if (this == &src) {
			return *this;
		}
		// 释放当前对象原来的资源
		// delete[] first_;
		for (T* p = first_; p != last_; p++) {
			allocator_.destroy(p);
		}
		allocator_.deallocate(first_);
		last_ = first_ = end_ = nullptr;

		// 开辟空间
		int size = src.end_ - src.first_;
		// first_ = new T[size];
		first_ = allocator_.allocate(sizeof(T) * size);

		// 拷贝有效元素
		int len = src.last_ - src.first_;
		for (int i = 0; i < len; i++) {
			allocator_.construct(first_ + i, src.first_[i]);
		}
		last_ = first_ + len;
		end_ = first_ + size;
		// 支持连续赋值
		return *this;
	}

	bool inline full() const {
		return last_ == end_;
	}

	bool inline empty() const {
		return first_ == last_;
	}

	bool inline size() const {
		return last_ - first_;
	}

	// 在容器末尾构造一个值为val的对象
	// 会发生引用折叠,参数是左值,则Types是Test&,引用折叠后,val是Test&
	// 参数是右值,则Types是Test&&,引用折叠后,val是Test&&
	template<typename Types>
	void push_back(Types&& val) {
		if (full()) {
			expand();
		}
		allocator_.construct(last_, std::forward<Types>(val));
		last_++;
	}

	// 从末尾删除元素
	void pop_back() {
		if (empty()) {
			return;
		}
		last_--;
		allocator_.destroy(last_);
	}

	T back() const {
		return *(last_ - 1);
	}

	template<typename... Types>
	void emplace_back(Types&&... args) {
		if (full()) {
			expand();
		}
		// 不管是左值引用变量还是右值引用变量,都是左值,需要用forward保持左值或右值的特性传递到空间配置器,再传递到元素的构造函数
		allocator_.construct(last_, std::forward<Types>(args)...);
		last_++;
	}

	void reserve(size_t n) {
		allocator_.allocate(sizeof(T) * n);
		end_ += n;
	}

private:
	void expand() {
		int size = end_ - first_;
		// T* tmp = new T [2 * size];
		T* tmp = allocator_.allocate(2 * sizeof(T) * size);
		for (int i = 0; i < size; i++) {
			allocator_.construct(tmp + i, first_[i]);
		}

		// delete[] first_;
		for (T* p = first_; p != last_; p++) {
			allocator_.destroy(p);
		}
		allocator_.deallocate(first_);

		first_ = tmp;
		last_ = first_ + size;
		end_ = first_ + 2 * size;
	}

	T* first_;   // 指向数组起始位置
	T* last_;    // 指向数组中有效元素的后继位置
	T* end_;     // 指向数组空间的后继位置
	Alloc allocator_;
};

int main(){
	Vector<Test> v;
	v.reserve(100);
	v.push_back(Test(10, "hello"));  // 右值拷贝构造,引用方式传递,临时对象析构后,容器底层的对象数据也不对
	cout << "=====================" << endl;
	v.emplace_back(11, "world");
	cout << "=====================" << endl;
	return 0;
}

在这里插入图片描述

空间配置器Allocator

template<typename T>
class Allocator {
public:
	// 开辟size字节
	T* allocate(size_t size) {
		return (T*)malloc(size);
	}

	void deallocate(void* p) {
		free(p);
	}

	template<typename... Types>
	void construct(T* p, Types&&... args) {
		// T就是Test
		// 当给emplace传入Test对象时,Types就是Test&或Test&&
		// 当给emplace传入Test构造所需参数时,Types就是int string,可变参表示参数的类型和数量都可变
		new (p) T(std::forward<Types>(args)...);
	}

	void destroy(T* p) {
		p->~T();
	}
};

基于可变参模板和引用折叠的emplace_back

template<typename... Types>
void emplace_back(Types&&... args) {
	if (full()) {
		expand();
	}
	// 不管是左值引用变量还是右值引用变量,都是左值,需要用forward
	allocator_.construct(last_, std::forward<Types>(args)...);
	last_++;
}

如果给emplace_back传入的左值,则Types是Test&,引用折叠后args就是Test&

如果给emplace_back传入的右值,则Types是Test&&,引用折叠后args就是Test&&

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bugcoder-9905

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值