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. 经典定长指令-修改EIP

    1.0x70~0x7F EIP无法像通用寄存器那样用mov来修改,只能通过类似于jz,JNB,JNE JBE,call等的跳转指令来进行修改 条件跳转,后跟一个字节立即数的偏移(有符号),共两个字节. ...

  2. test_1 计算字符串最后一个单词的长度,单词以空格隔开

    题目描述:计算字符串最后一个单词的长度,单词以空格隔开.  输入描述: 一行字符串,非空,长度小于5000. 输出描述: 整数N,最后一个单词的长度. #coding=utf-8 str = raw_ ...

  3. 【Java】回形数

    回形数 键盘读入一个整数n(1-20),以n为矩阵大小,把1,2,3,4,5-按顺时针螺旋的形式填入. import java.util.Scanner; public class HuiXingSh ...

  4. OSI/RM体系结构

    OSI/RM体系结构是第一个标准化的计算机网络体系结构.   它是针对广域网通信(也就是不同网络之间的通信)进行设计 的,将整个网络通信的功能划分为七个层次,由低到高分别是物理层(Physical L ...

  5. 《剑指offer》面试题56 - I. 数组中数字出现的次数

    问题描述 一个整型数组 nums 里除两个数字之外,其他数字都出现了两次.请写程序找出这两个只出现一次的数字.要求时间复杂度是O(n),空间复杂度是O(1). 示例 1: 输入:nums = [4,1 ...

  6. leetcode 83. 删除排序链表中的重复元素 及 82. 删除排序链表中的重复元素 II

    83. 删除排序链表中的重复元素 问题描述 给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次. 示例 1: 输入: 1->1->2 输出: 1->2 示例 2: 输入: ...

  7. JVM完整详解:内存分配+运行原理+回收算法+GC参数等

    不管是BAT面试,还是工作实践中的JVM调优以及参数设置,或者内存溢出检测等,都需要涉及到Java虚拟机的内存模型.内存分配,以及回收算法机制等,这些都是必考.必会技能. JVM内存模型 JVM内存模 ...

  8. 【记录一个问题】铁威马NAS存储中的python3,安装后找不到xml这个包

    如题 因为找不到xml,所以无法安装setuptools 因为无法安装setuptools,所以无法安装pip3 现在准备重新下载python3的源码自己编译. 铁威马的系统中还缺少libm这个库,导 ...

  9. C# 反射调用拓展类方法

    今天封装Protobuf封包时候遇到一个问题: Protobuf的反序列化方法MergeFrom,是写在扩展类里的:c#拓展类 通过反射获取不到这个方法,就没法使用Type来泛型封装... 然而仔细一 ...

  10. 学习OAuth 2.0

    认识OAuth 2.0 OAuth 2.0 是行业标准的授权协议. OAuth 2.0 专注于客户端开发人员的简单性,同时为 Web 应用程序.桌面应用程序.移动设备提供特定的授权流程. 应用场景 有 ...