#include <iostream>
#include <vector>
using namespace std;
class Test{
public:
Test(int a) {cout <<"Test(int)" << endl; }
Test(int a,int b) {cout <<"Test(int,int)" << endl; }
~Test(){cout << "~Test()"<< endl;}
Test(const Test&) {cout <<"Test(const Test&)" << endl; }
Test(Test&&) {cout <<"Test(Test&&)" << endl; }
};
template<typename T>
struct MyAllocator{
T* allocate(size_t size){
return (T*) malloc(size * sizeof(T));
}
template<typename... Types>
void construct(T* ptr , Types&&... args){
new (ptr) T(std::forward<Types>(args)...);
}
};
template< typename T,typename Alloc =MyAllocator<T>>
class Vector{
// 实参: 左值 =》 Types 为 Test& 右值引用变量不可能去引用一个左值,应该是 Test& +&& => Test&,怎args的类型为左值引用
// 右值 =》 Types 为 Test&& 右值引用变量右值 Test&& + && => Test&&
// emplace_back 接受多个类型参数,比如可能是引用,右值引用,int类型,多个int类型等,向容器插入对象
// 其底层是使用可变参数模板,完美转发,引用折叠实现的 , 首先emplace_back方法接收多个参数,然后调用容器的空间配置器,向已存在的空间上
// 使用定位new构建对象,而空间配置器的construct方法,也需要接受多个参数,在此过程中需要使用完美转发,保证左值引用还是左值引用,右值引用还是右值引用
// 然后将使用 定位new 构造对象new (ptr) T(std::forward<Types>(args)...); 根据接受参数的类型和个数选择构造对象的方法。
template<typename... Types>
void emplace_back(Types&&... args){
// 不管是左值引用变量还是右值引用变量,变量本身是个左值 ,传递过程中,要保证args的引用类型,使用完美转发
allocator_.construct(vec_+idx_ , std::forward<Types>(args)...);
idx_++;
}
template<typename Type>
void push_back(Type&& val){
allocator_.construct(vec_+idx_ , std::forward<Type>(val));
idx_++;
}
private:
T* vec_;
int size_;
int idx_;
Alloc allocator_;
};
#if 0
class Test{
public:
Test(int a) {cout <<"Test(int)" << endl; }
Test(int a,int b) {cout <<"Test(int,int)" << endl; }
~Test(){cout << "~Test()"<< endl;}
Test(const Test&) {cout <<"Test(const Test&)" << endl; }
Test(Test&&) {cout <<"Test(Test&&)" << endl; }
};
int main(){
Test t1(10);
vector<Test>v;
v.reserve(100);
cout << "============================"<< endl;
// 直接插入对象是没有区别的
v.push_back(t1);
v.emplace_back(t1);
cout << "============================"<< endl;
// 直接插入对象是没有区别的
//
//右值引用的拷贝构造函数通常比普通的拷贝构造函数更高效,因为右值引用允许在移动语义下进行操作,
// 而不是简单地复制数据。右值引用的拷贝构造函数通常会将资源的所有权从一个临时对象(右值)转移到目标对象上,而不是简单地复制数据。
//而对于右值引用的拷贝构造函数,可以直接将源对象的指针指向新的目标对象,而不需要进行额外的内存分配和释放。
//当临时对象的资源被移动到目标对象之后,临时对象仍然会被销毁。尽管资源的所有权已经转移,但是临时对象本身还是会经历析构过程。
v.push_back(Test(20));
v.emplace_back(Test(20));
cout << "============================"<< endl;
// 给emplace传入Test构造函数所需要的参数,会再容器底层构造对象
v.emplace_back(20);
v.emplace_back(20,30);
cout << "============================"<< endl;
return 0;
}
#endif
面试十六、emplace原理
最新推荐文章于 2025-05-03 06:58:41 发布