完整代码示例
来源:互联网 发布:计算面积的软件 编辑:程序博客网 时间:2024/06/04 19:49
完整代码示例
#ifndef LEFTVALUE_STRVEC_H#define LEFTVALUE_STRVEC_H#include <bits/allocator.h>#include <string>#include <memory>class StrVec {public: // 默认构造函数。 // (隐式地)默认初始化静态成员allocator1。 // (显示地)将指针成员初始化为nullptr,表示没有元素。 StrVec():element(nullptr), first_free(nullptr), cap(nullptr) { } StrVec(const StrVec &obj) { auto newdata = alloc_n_copy(obj.begin(), obj.end()); element = newdata.first; first_free = newdata.second; //由于alloc_n_copy分配的内存空间恰好容纳给定的元素,cap也指向最后一个构造的函数之后的位置。 cap = newdata.second; } StrVec& operator=(const StrVec &obj) { auto data = alloc_n_copy(obj.begin(), obj.end()); free(); element = data.first; first_free = cap = data.second; return *this; } //与拷贝构造函数不同,移动构造函数不分配任何新内存; //它接管给定的StrVec中的内存。 //在接管内存之后,它将给定对象中的指针都置为nullptr。 //这样就完成了从给定对象的移动操作,此对象将继续存在。 //最终,移动源对象会被销毁,意味着将在其上运行析构函数。 //Strvec的析构函数在first_free上调用deallocate。 //如果我们忘记了改变obj.first_free,则销毁移动后源对象就会释放掉我们刚刚移动的内存。 StrVec (StrVec &&obj) noexcept : element(obj.element), first_free(obj.first_free), cap(obj.cap) { obj.element = obj.first_free = obj.cap = nullptr; } StrVec&operator=(StrVec &&obj) noexcept { //直接检查this指针与obj的地址是否相同。 //如果相同,右侧和左侧运算对象指向相同的对象,我们不需要做任何事情。 //否则,我们释放左侧运算对象所使用的内存,并接管给定对象的内存。 //我们进行检查的原因是此右值可能是move调用的返回结果。 //我们不能在使用右侧运算对象的资源之前就释放左侧运算对象的资源(可能是相同的资源)。 if (this != &obj) { free(); element = obj.element; first_free = obj.first_free; cap = obj.cap; obj.element = obj.first_free = obj.cap = nullptr; } return *this; } ~StrVec() { free(); } //当我们调用push_back,实参类型决定了新元素是拷贝还是移动到容器中。 //这些调用的差别在于实参是一个左值还是一个右值。 //如果一个成员函数同时提供了拷贝和移动版本。 //第一个版本接受一个指向const的左值引用,我们可以将能转换为std::string类型的任何对象传递给第一个版本的push_back。 //此版本从其参数拷贝数据。 //第二版版本接受一个指向非const的右值引用。我们只可以传递给它非const的右值。 //此版本对于非const的右值是精确匹配的,因此当我们传递一个可修改的右值时,编译器会选择运行这个版本。 //此版本会从其参数窃取数据。 //一般来说,我们不需要为函数操作定义接受一个const X&&或是一个普通的X&参数的版本。 //当我们希望从实参窃取数据时,通常传递一个右值引用,为了达到这一目的,实参不能是const的。 //类似的,从一个对象进行拷贝的操作不应该改变该对象,因此通常不需要定义一个接受一个普通的X&参数的版本。 void push_back(const std::string &string1) { //调用chk_n_alloc确定有空间容纳新元素。 //如果需要,chk_n_alloc会调用reallocate。 chk_n_alloc(); allocator1.construct(first_free++, string1); } void push_bakc(std::string &&string2) { chk_n_alloc(); allocator1.construct(first_free++, std::move(string2)); } size_t size() const { return first_free - element; } size_t capacity() const { return cap - element; } std::string* begin() const { return element; } std::string* end() const { return first_free; }private: void chk_n_alloc() { if (size() == capacity()) reallocate(); } std::pair<std::string*, std::string*> alloc_n_copy(const std::string *b, const std::string *e) { //用尾后指针减去首元素指针,来计算需要多少空间。 auto data = allocator1.allocate(e - b); //pair类型对象的first成员指向分配的内存的开始位置。 //second成员则是uninitialized_copy的返回值,此值是一个指针,指向最后一个构造元素之后的位置。 return {data, std::uninitialized_copy(b, e, data)}; }; void free() { //我们传递给deallocate的指针必须是之前某次调用allocate调用所返回的指针。 //因此,在调用deallocate之前我们首先检查element是否为空。 if (element) { //调用allocator的destory成员,从构造的尾元素开始,到首元素为止,逆序销毁所有元素。 //destory函数会运行string的析构函数。string的析构函数会释放string自己分配的内存空间。 for (auto p = first_free; p != element; ) allocator1.destroy(--p); //调用deallocate来释放本StrVec对象分配的内存空间。 allocator1.deallocate(element, cap - element); } } void reallocate() { //首先调用allocate分配新内存空间。 //我们每次重新分配内存时都会将StrVec的容量加倍。 //如果StrVec为空,我们将分配容纳一个元素的空间。 auto newcapacity = size() ? 2*size() : 1; auto newdata = allocator1.allocate(newcapacity); //dest指向构造新string的内存 //elem指向原数组中的元素 auto dest = newdata; auto elem = element; //for循环遍历每个已有元素,并在新内存空间中construct一个对应元素 //我们每次用后置递增运算符将dest(和elem)推进到各自数组中的下一个元素。 //调用move返回的结果会令construct使用string的移动构造函数。 //由于我们使用了移动构造函数,这些string管理的内存将不会被拷贝。 //相反,我们构造的每个string都会从elem指向的string那里接管内存的所有权。 for (size_t i = 0; i != size(); ++i) allocator1.construct(dest++, std::move(*elem++)); //元素移动完毕后,调用free销毁旧元素并释放StrVec原来使用的内存。 //我们不知道旧StrVec内存中的string包含什么值,但我们保证对它们执行string的析构函数是安全的。 free(); //更新指针 element = newdata; first_free = dest; cap = element + newcapacity; } static std::allocator<std::string> allocator1; std::string *element;//指向数组首元素 std::string *first_free;//指向数组第一个空闲单元的元素 std::string *cap;//指向数组尾后位置};#endif //LEFTVALUE_STRVEC_H
阅读全文
0 0
- 完整代码示例
- Ajax入门示例完整代码
- C# AccessHelper 完整示例代码
- C# SqlServerHelper 代码完整示例
- 一篇完整的doxygen示例代码
- Javax Mail发送邮件完整代码示例
- 第十课 完整的Lua代码示例
- 使用secondary sort实现数据关联 完整示例代码
- 利用ffmpeg来进行视频解码的完整示例代码
- Flex 4.6 XML搜索、匹配示例,完整代码
- pthread_cond_timedwait按相对时间等待超时完整示例代码
- 用smarty实现简单分页的示例完整代码
- opengl编程指南示例程序2-15完整代码
- opencv图像处理常用完整示例代码总结
- TensorFlow mnist多层卷积网络官方示例完整代码
- snmp4j完整示例
- AdvancedDataGrid完整示例
- AdvancedDataGrid完整示例
- Arrays用法整理
- 怎么在VirtualBox上装电脑公司的win7系统
- 实时调试WebDriver代码
- 小朋友学C语言(15):“变量==常量”与“常量==变量”的区别
- week_5_ Integer to Roman
- 完整代码示例
- Error: Could not link: /usr/local/share/doc/homebrew
- nginx安装配置
- spring data jpa 自定义接口实现
- 小朋友学C语言(16):斐波那契数列的非递归实现
- 小朋友学C语言(17):斐波那契数列的递归实现
- Android学习之调用摄像头和相册
- hadoop搭建之ssh配置
- nw打包app流程