• <四>理解空间配置器allocator, 优化STL 中的Vector


    .在上一节我们实现的 MyVector存在哪些问题?

    问题1

    现在有Student类
    class Student{
       public:
       Student(){cout<<"构造Student对象"<Student(){cout<<"析构Student对象"<private:
           int   age
           char  NAME[20];
           char  *pAddress;
    }
    
    MyVector v1[1000];
    

    我只是希望创建一个能放1000个Student 的Vector,但是开始并不放任何内容,但是发现编译器除分配了1000个student对象的空间,还创建了1000个对象,
    在main函数结束后再析构这1000个对象,这就是问题1,这1000个对象并不是我需要的,
    原因如下,在MyVector的构造函数中 T * _tep = new T[size](); 这个new除了分配空间,还会调用对象的构造函数完成对象的初始化
    换句话说就是 空间的分配和对象的创建联系在了一起,这个非常不合理,我们需要把他分开,我希望是需要帮我开辟空间即可,不希望帮我创建1000个对象

    问题2关于析构函数

    //析构函数
    ~MyVector() {
    	if (Empty() == false) {
    		delete[]first;
    		first = nullptr;
    		last  = nullptr;
    		end   = nullptr;
    		cout << "析构Vector,堆地址"<

    实际情况时,我的vector是可以放1000个student对象的容器,但是实际里面可能暂时只放了100个对象,
    而delete[] first,是把整个vector中的1000个student对象都调用析构函数,这也不合理,我们只需要析构有效
    数量的对象然后再释放first指针指向的堆内存

    问题3 关于添加元素和删除元素

    student s1;
    student s2;
    student s3;
    
    MyVector v1(1000);// 这一句编译器会先分配1000个student对象空间,然后再调用1000次构造函数创建1000个student对象
    
    
    v1.pushback(s1);
    //上面这句话会调用赋值函数,会将s1的内容赋值给已经在堆上的student对象,但实际上我想要的是,只需要将分配好的空间
    //给我,我使用拷贝构造,将s1拷贝构造出来一个对象放在你给的空间上,这是个问题
    
    
    v1.popBack(){
        this->last--;
    }
    //上面的pop最后一个studnet对象,只是将最后一个元素的指针前移动一个student空间大小,并没有去析构这个对象,
    //而且这个弹出的student对象里面有一个char *pAddress 指向了外部堆内存资源,这个内存自己没有去释放的话,
    //造成了内存泄漏了. 这个也是个问题,所以说我们要去析构这个弹出的student对象但是不能使用delete方式delete这个对象,为什么呢?
    //因为delete调用完析构函数,他还要free释放内存空间,但是这个空间属于Vector的,释放掉了这一小块空间,我们后面就无法在使用了,
    //**所以一句话就是我们从vector中删除一个对象时,只做析构这个对象但不释放他的内存空间,需要把对象的析构和内存的释放分开**
    

    以上三个问题使得我们不能采用上一篇文字中的方式写Vector,这就是引入了容器的空间配置器的原因

    容器的空间配置器做了4件事
    内存开辟/内存释放 对象创建/对象析构

    在上一遍的基础上加入空间配置器,代码如下

    #include 
    using namespace std;
    
    
    class student {
    
    public:
    
    	student(int _age, char * _pname): 
    		age(_age),
    		pName(_pname){  
    	}
    
    	student(const student & _rvalue) {
    		this->age = _rvalue.age;
    		this->pName = new char[20];
    		strcpy(this->pName, _rvalue.pName);
    	}
    
    	student & operator =(const student & _rvalue) {
    		
    		if (this == &_rvalue) { return *this; }
    		
    		delete[]this->pName;
    		this->pName = nullptr;
    
    		this->age = _rvalue.age;	
    		this->pName = new char[20];
    		strcpy(this->pName, _rvalue.pName);
    	}
    	~student() {
    		delete[] this->pName;
    		this->pName == nullptr;
    		cout << "student 析构函数被执行,当前对象地址是" << this << endl;
    	}
    
    
    private:
    	int age;
    	char *pName;
    };
    
    
    template<typename T>
    class MyAllocate {
    
    public:
    
    	T * allocate(int size) {
    		return (T *)malloc(sizeof(T)*size);
    	}
    
    	//根据指定的地址,释放内存空间
    	void delAllocate(T *p) {
    		free(p);
    	}
    
    	//在p指针指定的位置,根据指定的对象创建新对象
    	void construct(T * p, const T & _rValue) {
    		new (p) T(_rValue);
    	}
    
    	//析构指定对象,但不释放内存空间
    	void destory(T * p) {
    		if (p != nullptr) {
    			p->~T();
    		}
    	}
    
    
    
    };
    
    
    
    template<typename T, typename Allocate=MyAllocate>
    class MyVector2 {
    
    public:
    
    	//构造函数
    	MyVector2(int size = 10, const Allocate & _allocate = MyAllocate()) : allocator(_allocate) {
    		
    		first = allocator.allocate(size);
    		last = first;
    		end = first + size;
    		cout << "MyVector2 构造函数,构建数量=" << size <<"堆空间构造起始地址="<"结束地址=" << endl;
    	}
    
    	//拷贝构造,根据指定的 MyVector2 创建新的MyVector2
    	MyVector2(const MyVector2 & _rValue) {
    		
    		//1:根据原MyVector2的Size 申请内存空间
    		first = allocator.allocate(_rValue.Size());
    		last = first;
    		end = first + __rValue.Size();
    
    		//2:根据原MyVector2内的对象,在第1步申请的堆内存中构造对象
    		T *tep = _rValue.first;
    		while (tep<_rValue.end)
    		{			
    			allocate.construct(last, *tep)
    			last++;
    			tep++;
    		}
    		cout << "MyVector2 拷贝构造函数,构建数量" << __rValue.Size() << endl;
    	}
    
    	//赋值函数
    	MyVector2 & operator=(const MyVector2 & _rValue) {
    		if (this == &_rValue) { return *this;}
    
    		//1:先析构目标Vector2中所有的对象
    		T * tep = first;
    		while (tep < last) {
    			allocator.destory(tep);
    			tep++;
    		}
    
    		//2:释放目标Vector2的内存空间
    		allocator.delAllocate(first);
    
    		//3:根据原MyVector2的size 申请新的内存空间
    		int rSize = _rValue.Size();
    		T * _head allocator.allocate(rSize);
    		first = _head;
    		last  = _head;
    		end   = first + rSize;
    
    		//4:根据原MyVector2中的有效的对象在 第3步申请的内存空间上构建对象
    
    		T *tep = _rValue.first;
    		while (tep<_rValue.end)
    		{
    			allocator.construct(last, *tep)
    			last++;
    			tep++;
    		}
    	
    		cout << "MyVector2 赋值函数,构建数量" << rSize << endl;
    
    	}
    	
    	//在已经申请空间的位置 添加值
    	void pushBack(const T & _addValue) {
    		if (Full()) {
    			Expend();//两倍扩容
    		}
    		//在指定地址空间 构建对象
    		allocator.construct(last, _addValue);
    		cout << "pushBack 元素,内存地址=" << last << endl;
    		last++;
    		
    	}
    
    	//弹出
    	void popBack() {
    		if (Empty()) { return; }
    		//1:只析构指定位置对象,但不释放空间
    		allocator.destory(last - 1);
    		cout << "popBack元素,其内存地址=" << (last - 1) << endl;
    
    		//2:移动last指针
    		last--;
    	
    	}
    	
    	//是否为空
    	bool Empty() { return first == last; }
    	
    	//是否满
    	bool Full() { return  last  == end;  }
    
    	int  Size() { return   end - first; }
    
    	//容器扩容
    	void Expend() {
    
    		int newSize = this->Size() * 2;//两倍扩容
    		
    		//1:申请新的空间
    		T * head = allocator.allocate(newSize);
    		T * tep_first = head;
    		T * tep_last =  head;
    		T * tep_end  = first + newSize;
    
    		cout << "两倍扩容,新的堆内存起始地址=" << head << endl;
    
    		//2:将原来有效的对象 赋值到新的堆空间上
    		T * tep = first;
    		while (tep < last) {
    			allocator.construct(tep_first,*tep);
    			cout << "两倍扩容,赋值对象,原对象地址=" << tep <<"目标对象地址="<//3:析构原堆空间上有效的对象
    		while (tep < last) {
    			allocator.destory(tep);
    			cout << "两倍扩容,析构对象,对象地址=" << tep << endl;
    			tep++;
    			
    		}
    
    		//4:释放堆上的空间
    		allocator.delAllocate(first);
    		cout << "两倍扩容,销毁原空间" << first << endl;
    		first = head;
    		last  = tep_last;
    		end   = first + newSize;
    
    	}
    
    
    	void showVectorInfo() {
    		T * tep = first;
    		while (tep < last)
    		{
    			cout << "打印Vector中有效对象地址=" << tep << endl;
    			tep++;
    		}
    	}
    
    
    private:
    	T * first;
    	T * last;
    	T * end;
    	Allocate allocator;
    
    };
    
    
    
    int main() {
    
    	MyVector2> v(4);
    
    	student s1(20, "zs1");
    
    	v.pushBack(s1);
    
    	student s2(22, "zs2");
    
    	v.pushBack(s2);
    
    	student s3(23, "zs3");
    
    	v.pushBack(s3);
    
    
    	student s4(24, "zs4");
    
    	v.pushBack(s4);
    
    	v.showVectorInfo();
    
    
    	student s5(25, "zs5");
    
    	v.pushBack(s5);
    	
    	v.showVectorInfo();
    
    	system("pause");
    
    	return 1;
    }
    
  • 相关阅读:
    2022年整理LeetCode最新刷题攻略分享(附中文详细题解)
    【JavaWeb】WEB容器
    ES6新特性之Symbol的数据类型
    centost7下安装oracle11g 总结踩坑
    Neo4J
    【设计模式】JAVA Design Patterns——Factory Method(虚拟构造器模式)
    Hadoop学习总结(Shell操作)
    如何在 R 中对模型性能执行交叉验证
    tkinter滚动事件详解
    一文带你彻底拿下a,b两点间等效电阻
  • 原文地址:https://www.cnblogs.com/erichome/p/16906631.html