c++ StrVec等效vector<string>的类

知识点

  1. 静态成员变量要在类外定义和初始化
  2. allocator类是使用和uninitialized_copy的配合使用,实现string空间的分配和strings数据的拷贝(拷贝string的时候调用的string的拷贝构造函数,string的拷贝构造函数会拷贝string指向的char数据)。
  3. 拷贝构造函数需要拷贝StrVec类的成员也要拷贝StrVec指向的string。
  4. 拷贝赋值运算符,拷贝了右侧对象StrVec指向的数据,同时销毁左侧StrVec指向的数据
  5. 重新分配空间的时候,只拷贝string而不拷贝string指向的空间,通过alloc.construct(dst++, str::move(src++))实现。

alloc_n_copy()和free()函数

  在拷贝构造函数,拷贝赋值运算符都要用到空间分配和指向数据的拷贝,所以定义一个alloc_n_copy()函数

  在拷贝赋值运算符和析构函数中都要释放指向的空间,所以定义free()函数

  一般类中都应该定义这两个private函数,方便时序拷贝构造函数、拷贝赋值运算符和析构函数。

StrVec.h

#include <string>
#include <memory>
#include <utility> // pair move
#include <initializer_list> // initializer_list
#include <algorithm> // for_each #ifndef STRVEC__H
#define STRVEC__H using namespace std; class StrVec {
public:
StrVec():b(nullptr),e(nullptr),cap(nullptr){}
StrVec(const StrVec &);
StrVec &operator=(const StrVec &);
~StrVec(); void push_back(const string &);
size_t size() const {return e - b;}
size_t capacity() const {return cap - b;}
void reserve(const size_t &);
void resize(const size_t &);
void resize(const size_t &, const string &);
string *begin() const {return b;}
string *end() const {return e;} private:
static allocator<string> alloc;
void chk_n_alloc() {if(size() == capacity()) reallocate();}
pair<string*,string*> alloc_n_copy(const string*, const string*);
void free();
void reallocate();
string *b;
string *e;
string *cap;
} #endif

StrVec.cpp

#include "StrVec.h"

// 静态成员变量定义和默认初始化。
// 静态成员变量不属于任何对象,不能在类的构造函数中被构造和初始化,必须在类的外部定义和初始化
// 不要重复写static
// 静态函数可以在类内部定义(没有初始化一说)
allocator<string> StrVec::alloc; // 列表初始化的构造函数
// initializer_list元素是const的,但是initializer_list不是const的,要接收{"abc","hello"}这样为const的列表
// 要求initializer_list也为const类型的。
StrVec:StrVec(const initializer_list<string> &strs) {
auto p = alloc_n_copy(strs.begin(), strs.end());
b = p.first;
e = cap = p.second;
} // 分配能容纳拷贝数据的空间,并把数据拷贝到新空间。
// 返回分配空间的首尾地址
// 这里是真实的数据拷贝。
pair<string*,string*> Strvec::alloc_n_copy(const string *b_ptr, const string *e_ptr) {
auto p = alloc.allocate(e_ptr - b_ptr);
return {p, uninitialized_copy(b_ptr, e_ptr, p)};// 调用了string的拷贝构造函数,拷贝构造函数中,重新为string指向的char分配了空间并拷贝。
} // 释放StrVec分配的空间
// 先destroy StrVec中的成员,再把整个分配空间都释放(deallocate)
void StrVec::free() {
if(e) {
for(auto p = e; p != b;)
alloc.destroy(--p);
alloc.deallocate(cap-b);
}
} // 使用for_each实现的free函数
//void StrVec::free() {
// if(e) {
// for_each(b, e, [](string &str)->void{destroy(&str);});
// alloc.deallocate(b, cap - b);
// }
//} // 重新分配更多的空间(string空间),并把原数据(char)移动到新空间中
// 利用string的移动函数,可以不要拷贝string执行的char数据,而只拷贝string指向char数据的首指针?
// 这里只分配string的空间,而不分配string指向char的空间
// 移动后,销毁原string的空间,而不销毁string指向char的空间,应为char的空间被新的string指向了。
void StrVec::reallocate() {
size_t newcapacity = size() ? 2*size() : 1; // 如果有数据就分配原来两倍数据的空间,如果没有就只分配1个string
auto p = alloc.allocate(newcapacity);
auto dst = p; // dst和src要递增,要保存一个p和b的备份用于拷贝string成员。
auto src = b;
for(size_t i=0; i != size(); ++i)
alloc.construct(dst++, std::move(*src++));
b = p;
e = dst;// p + size();
cap = p + newcapacity; } // 拷贝构造函数
// 拷贝StrVec指向的string, 同时拷贝一下StrVec的成员变量。
// 这个是真实的拷贝,alloc_n_copy拷贝StrVec指向的string,uninitialized_copy拷贝string指向的char
StrVec::StrVec(const StrVec &s) {
auto p = alloc_n_copy(s.begin(), s.end());
b = p.first;
e = cap = p.second;
} // 拷贝赋值运算符
// 真实的拷贝右侧对象指向的数据和成员变量到左侧对象
// 释放左侧对象指向空间,比拷贝构造函数多一个释放指向空间的过程。
StrVec &StrVec::operator=(const StrVec &s) {
auto p = alloc_n_copy(s.begin(), s.end());
free();
b = p.first;
e = cap = p.second;
} // 析构函数,释放StrVec指向空间,成员对象会自动析构。
StrVec::~StrVec() {
free();
} // 往StrVec中添加一个string
// 先检查还有没没有构造的空间,没有就分配一些,然后在第一个没有构造的空间上构造string.
void StrVec::push_back(const string &str) {
chk_n_alloc();
alloc.construct(e++, str);
} // 如果size>n,销毁后面的size-n个数据
// 如果size<=n,在模块构造n-size个数据
void StrVec::resize(const size_t &n) {
if(n > capacity()) { // n > capacity,要使用的空间比现有空间多,就要分配空间
auto p = alloc.allocate(n);
auto dst = p;
auto src = b;
size_t i = 0;
for(; i != size(); ++i)
alloc.construct(dst++, std::move(src++));
for(; i != n; ++i)
alloc.construct(dst++);//使用string的默认构造函数构造。
free();
b = p;
e = cap = dst;
} else if(n > size()) { // size < n < capacity,要使用的空间比现有少,但是比使用的空间多,在现有的空间上构造数据即可
while(e < b+n)
alloc.construct(e++);
} else { // n < size,要使用的空间比使用的还有少,要销毁模块的数据。
while(e > b+n)
alloc.destroy(--e);
}
} // 如果size>n,销毁后面的size-n个数据
// 如果size<=n,在模块构造n-size个数据
void StrVec::resize(const size_t &n, const string &str) {
if(n > capacity()) { // n > capacity,要使用的空间比现有空间多,就要分配空间
auto p = alloc.allocate(n);
auto dst = p;
auto src = b;
size_t i = 0;
for(; i != size(); ++i)
alloc.construct(dst++, std::move(src++));
for(; i != n; ++i)
alloc.construct(dst++, str);//使用string的拷贝构造函数。
free();
b = p;
e = cap = dst;
} else if(n > size()) { // size < n < capacity,要使用的空间比现有少,但是比使用的空间多,在现有的空间上构造数据即可
while(e < b+n)
alloc.construct(e++, str);
} else { // n < size,要使用的空间比使用的还有少,要销毁模块的数据。
while(e > b+n)
alloc.destroy(--e);
}
} // 修改容器的容量,如果capacity()<n时会分配新空间,但是capacity()>=n时什么也不做
void StrVec::reserve(const size_t &n) {
if(capacity() < n) {
auto p = alloc.allocate(n);
auto dst = p;
auto src = b;
for(size_t i=0; i<size(); ++i)
alloc.const(dst++, std::move(src++));
free();
b = p;
e = dst;
cap = b + n;
}
}

测试程序

string a = "name";
string b = "hello";
string c = "world";
StrVec str; // 测试默认构造函数
str.push_back(a); // 测试push_back
str.push_back(b);
str.push_back(c);
for(const auto &v : str)
cout<<v<<endl; // 输出3行,name/hello/world StrVec str2(str); // 测试拷贝构造函数
for(const auto &v : str2)
cout<<v<<endl; // 输出3行,name/hello/world StrVec str3;
str3 = str; // 测试拷贝构赋值运算符
for(const auto &v : str3)
cout<<v<<endl; // 输出3行,name/hello/world cout<<"size:"<<str.size()<<",capacity:"<<str.capacity()<<endl; // 输出size:3,capacity:4
str.reserve(10);
cout<<"size:"<<str.size()<<",capacity:"<<str.capacity()<<endl; // 输出size:3,capacity:10 str.resize(10);
cout<<"size:"<<str.size()<<",capacity:"<<str.capacity()<<endl; // 输出size:10,capacity:10
for(const auto &v : str)
cout<<v<<endl; // 输出3行,name/hello/world和7个空行 str.resize(2);
cout<<"size:"<<str.size()<<",capacity:"<<str.capacity()<<endl; // 输出size:2,capacity:10
for(const auto &v : str)
cout<<v<<endl; // 输出3行,name/hello str.resize(12,"xx");
cout<<"size:"<<str.size()<<",capacity:"<<str.capacity()<<endl; // 输出size:2,capacity:10
for(const auto &v : str)
cout<<v<<endl; // 输出3行,name/hello和10行"xx" StrVec str4 = {"hello", "list", "strVec"}; // 测试列表构造函数
for(const auto &v : str4)
cout<<v<<endl; // 输出3行,hello/list/strVec

c++ StrVec等效vector(string)的类的更多相关文章

  1. STL review:vector & string & map & struct

    I.vector 1.头文件:#include<vector>                        //容器vector是一个能实现随机存取.插入删除的动态数组,还可以当栈使. ...

  2. C++ Split string into vector<string> by space

    在C++中,我们有时候需要拆分字符串,比如字符串string str = "dog cat cat dog"想以空格区分拆成四个单词,Java中实在太方便了,直接String[] ...

  3. 单独删除std::vector <std::vector<string> > 的所有元素

    下面为测试代码: 1.创建 std::vector< std::vector<string> > vc2; 2.初始化 std::vector<string> vc ...

  4. C++自定义String字符串类,支持子串搜索

    C++自定义String字符串类 实现了各种基本操作,包括重载+号实现String的拼接 findSubStr函数,也就是寻找目标串在String中的位置,用到了KMP字符串搜索算法. #includ ...

  5. 编写程序,将来自文件中的行保存在一个vector<string>,然后使用一个istringstream 从vector中读取数据,每次读一个单词

    #include<fstream> #include <vector> #include<string> #include<iostream> #inc ...

  6. String工具类

    String工具类 问题描述 MAVEN依赖 代码成果 问题描述 很多时候我们需要对字符串进行很多固定的操作,而这些操作在JDK/JRE中又没有预置,于是我们想到了apache-commons组件,但 ...

  7. vector(char*)和vector(string)

    vector<char*> ch; vector<string> str; for(int i=0;i<5;i++) { char *c=fun1();//通过这个语句产 ...

  8. Effective STL 学习笔记: 多用 vector & string

    Effective STL 学习笔记: 多用 vector & string 如果可能的话, 尽量避免自己去写动态分配的数组,转而使用 vector 和 string . 原书作者唯一想到的一 ...

  9. PKU 1035 Spell checker(Vector+String应用)

    题目大意:原题链接 1输入一个字符串,如果字典中存在该字符串,直接输出,否则; 2.删除,替换,或插入一个字母.如果使得输入字符串==字典中已经有的单词,输出该单词,否则. 3.直接输入下一个字符串, ...

随机推荐

  1. Linux上天之路(三)之Linux系统目录

    1. Linux设计思想 1) 程序应该小而专一,程序应该尽量的小,且只专注于一件事上,不要开发那些看起来有用但是90%的情况都用不到的特性: 2) 程序不只要考虑性能, 程序的可移植性更重要,she ...

  2. vue iview element-ui兼容IE11浏览器

    首先安装babel-polyfill npm install babel-polyfill --save-dev 入口文件引用,在main.js中引用 import 'babel-polyfill' ...

  3. 论文翻译:2020_FLGCNN: A novel fully convolutional neural network for end-to-end monaural speech enhancement with utterance-based objective functions

    论文地址:FLGCNN:一种新颖的全卷积神经网络,用于基于话语的目标函数的端到端单耳语音增强 论文代码:https://github.com/LXP-Never/FLGCCRN(非官方复现) 引用格式 ...

  4. Keil MDK STM32系列(五) 使用STM32CubeMX创建项目基础结构

    Keil MDK STM32系列 Keil MDK STM32系列(一) 基于标准外设库SPL的STM32F103开发 Keil MDK STM32系列(二) 基于标准外设库SPL的STM32F401 ...

  5. java运行时创建对象

    有很多场景需要运行时创建对象,比如Copy对象到指定类型的对象中,比如根据指定的字段和值创建指定类型的对像.使用JDK自带的反射(java.lang.reflect)或者自省(java.beans.I ...

  6. Genymotion安装apk问题,不能部署Genymotion-ARM-Translation_v1.zip

    把Genymotion-ARM-Translation_v1.zip拖进去提示 Files successfully copied to: /sdcard/Download 但还是不能安装apk 解决 ...

  7. thinkpad s5 电源功率不足提示

    相关答案 作者:路灯瓜 链接:https://www.zhihu.com/question/47551448/answer/122578101 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权 ...

  8. IDEA2017 maven Spark HelloWorld项目(本地断点调试)

    作为windows下的spark开发环境 1.应用安装 首先安装好idea2017 java8 scalaJDK spark hadoop(注意scala和spark的版本要匹配) 2.打开idea, ...

  9. 感恩陪伴 HelloGitHub 定制的红包封面

    距离放假越来越近了,我们更文的频率也越来越低了. 先别打!听我解释... 我真没偷懒,我是去研究今年的「微信红包封面」玩法了. 这不去年,我们制作的 HelloGitHub 专属红包封面,很多粉丝都说 ...

  10. log4j学习记录以及相关配置(精简版)

    使用log4j时关键配置 log4j的maven依赖 <dependency> <groupId>log4j</groupId> <artifactId> ...