13.39 编写你自己版本的StrVec,包括自己版本的reserve,capacity(参见9.4节,第318页)和resize(参见9.3.5节,第314页)

13.40 为你的StrVec类添加一个构造函数,它接受一个initializer_list<string>参数

这是StrVec.h

#pragma once

#include<string>
#include<memory>
#include<initializer_list>
using namespace std; class StrVec
{
public:
StrVec():
elements(nullptr),first_free(nullptr),cap(nullptr){}
StrVec(initializer_list<string> iLStr);//构造函数,接受一个initializer_list<string>参数
StrVec(const StrVec&);
StrVec& operator=(const StrVec&);
~StrVec();
string& operator[](size_t n)const //重载下标运算符,用来实现TextQuery和QueryResult对StrVec的使用
{
return *(elements + n);
}
void push_back(const string&);
size_t size()const { return first_free - elements; }
size_t capacity()const { return cap - elements; }
string *begin()const { return elements; }
string *end()const { return first_free; }
void reserve(size_t n);//分配至少容纳n个元素的空间
void resize(size_t n);//调整容器的大小为n个元素。若n<size(),则多出的元素被丢弃
//若必须添加新元素,对新元素进行值初始化
void resize(size_t n,string str);//调整容器的大小为n个元素,任何新添加的元素都初始化为值str
private:
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 *elements;
string *first_free;
string *cap;
}; void StrVec::push_back(const string &s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
} pair<string*,string*>
StrVec::alloc_n_copy(const string *b, const string *e)
{
auto data = alloc.allocate(e - b);
return{ data,uninitialized_copy(b,e,data) };
} void StrVec::free()
{
if (elements) {
for (auto p = first_free;p != elements;)
alloc.destroy(--p);
alloc.deallocate(elements, cap - elements);
}
} StrVec::StrVec(const StrVec &s)
{
auto newdata = alloc_n_copy(s.begin(), s.end());
elements = newdata.first;
first_free = cap = newdata.second;
} StrVec::~StrVec() { free(); } StrVec &StrVec::operator=(const StrVec &rhs)
{
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
} void StrVec::reallocate()
{
auto newcapacity = size() ? * size() : ;
auto newdata = alloc.allocate(newcapacity);
auto dest = newdata;
auto elem = elements;
for (size_t i = ;i != size();++i)
alloc.construct(dest++, move(*elem++));
free();
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
} void StrVec::reserve(size_t n)//分配至少容纳n个元素的空间
{
if (n > capacity())//如果n大于capacity()才会从新分配空间
{
auto newdata = alloc.allocate(n);//重新分配n个空间,newdata为新分配的空间的首地址
auto dest = newdata;
auto elem = elements;
for (;elem != first_free;) //为新分配的空间调用construct来实现string的构造,采用move调用的是移动构造函数
alloc.construct(dest++, move(*(elem++)));
free(); //元素的移动完成,释放原有的空间
elements = newdata; //为指针赋予新的值
first_free = dest;
cap = elements + n;
}
else return;
} void StrVec::resize(size_t n)//调整容器的大小为n个元素。若n<size(),则多出的元素被丢弃
//若必须添加新元素,对新元素进行值初始化
{
if (n <= size()) //如果n<size()则,应该对n后面的所有已经构造的元素调用destroy(),即多出的元素被丢弃
{
for (;first_free != elements + n;)
alloc.destroy(--first_free);
}
else
{
if (n > capacity())
{
reserve(n); //因为n>capacity(),所以一定会分配新的空间
}
for (;first_free != elements + n;) //添加新的元素,对新的元素进行值初始化
alloc.construct(first_free++, string(""));
}
} void StrVec::resize(size_t n,string str)//调整容器的大小为n个元素,任何新添加的元素都初始化为值str {
if (n <= size()) //如果n<size()则,应该对n后面的所有已经构造的元素调用destroy(),即多出的元素被丢弃
{
for (;first_free != elements + n;)
alloc.destroy(--first_free);
}
else
{
if (n > capacity())
{
reserve(n); //因为n>capacity(),所以一定会分配新的空间
}
for (;first_free != elements + n;) //添加新的元素为str
alloc.construct(first_free++, str);
}
} StrVec::StrVec(initializer_list<string> iLStr)//构造函数,接受一个initializer_list<string>参数
{
auto newdata = alloc_n_copy(std::begin(iLStr), std::end(iLStr));//调用alloc_n_copy函数,返回一个pair<string*,string*>
elements = newdata.first; //pair的第一个元素为新分配空间的地址
first_free = cap = newdata.second; //pair的第二个元素为新分配空间的最后一个元素之后的地址
}

下面是主函数,用来验证程序的正确性

// 13.5.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#define _SCL_SECURE_NO_WARNINGS
#include"StrVec.h"
#include<string>
#include<iostream>
#include<memory>
using namespace std; int main()
{
StrVec sv({ "li","dandan","is" });
cout << sv.size() << " " << sv.capacity() << endl;
sv.resize(, "handsome");
cout << sv.size() << " " << sv.capacity() << endl;
sv.resize();
cout << sv.size() << " " << sv.capacity() << endl;
sv.resize();
cout << sv.size() << " " << sv.capacity() << endl;
sv.reserve();
sv.push_back("handsome");
cout << sv.size() << " " << sv.capacity() << endl;
return ;
}

C++primer 练习13.39的更多相关文章

  1. C++primer 练习13.44

    13.44:编写标准库string类的简化版本,命名为String.你的类应该至少有一个默认构造函数和一个接受C 风格字符串指针参数的构造函数.使用allocator为你的String类分配所需内存 ...

  2. 【C++ Primer 第13章】2. 拷贝控制和资源管理

    拷贝控制和资源管理 • 类的行为像一个值.意味着它应该有自己的状态,当我们拷贝一个像值得对象时,副本和原对象是完全独立的,改变副本不会对原对象有任何影响. • 行为像指针的类则共享状态.当我们拷贝一个 ...

  3. 【C++ Primer 第13章】1. 拷贝控制、赋值和销毁

    拷贝控制.赋值和销毁 如果一个构造函数的第一个参数是自身类的引用,且额外的参数都有默认值,则此构造函数是拷贝控制函数(拷贝构造函数不应该是explicit的). 如果我们没有为一个类定义拷贝构造函数, ...

  4. 【C++ Primer 第13章】3. 交换操作

    交换操作 class HasPtr { friend void swap(HasPtr &rhs, HasPtr &yhs); //其他成员定义 }; void swap(HasPtr ...

  5. [C++ Primer] : 第13章: 拷贝控制

    拷贝, 赋值与销毁 当定义一个类时, 我们显示地或隐式地指定在此类型的对象拷贝, 移动, 赋值和销毁时做什么. 一个类通过定义5种特殊的成员函数来控制这些操作, 包括: 拷贝构造函数, 拷贝赋值运算符 ...

  6. C++ primer chapter 13

    拷贝 赋值 销毁 拷贝构造函数 如果一个构造函数第一个参数是自身的引用,而且任何额外参数都有默认值,则此构造函数是拷贝构造函数拷贝构造函数的第一个类型必须是引用:如果参数不是引用类型,那么调用不会成功 ...

  7. C++primer 练习13.36

    #pragma once #include<string> #include<set> using namespace std; class Message { friend ...

  8. 【C++ Primer 第13章】6.对象移动

    右值引用 左值和右值 (1)两者区别: ①左值:能对表达式取地址.或具名对象/变量.一般指表达式结束后依然存在的持久对象. ②右值:不能对表达式取地址,或匿名对象.一般指表达式结束就不再存在的临时对象 ...

  9. 【C++ Primer 第13章】5. 动态内存管理类

    StrVec类的设计 [题目描述]:我们将实现标准库vector类的一个简化版本,我们所做的一个简化是不使用模板,我们类只用于string,因此,它被命名为StrVec. #include<io ...

随机推荐

  1. ASP.NET MVC 程序 报错“CS0012: 类型“System.Data.Objects.DataClasses.EntityObject”在未被引用的程序集中定义”的解决办法

    运行MVC程序,具体报错信息如下: 解决方法: 打开Web.config在assemblies下加入<add assembly="System.Data.Entity, Version ...

  2. 在eclipse中的tomcat内存设置

    设置步骤如下: 1.点击eclipse上的debug图标旁边的下拉箭头 2.然后选择Run Configurations, 3.系统弹出设置tomcat配置页面,在Argument中末尾添加参数中的V ...

  3. C转义字符

    C语言输出特殊字符 C语言转义字符意义大体同于前面的C#转义字符,这里列出用c语言,输出%d.\n等特殊字符的方法. #include <stdio.h> int main() { pri ...

  4. CentOS 7.0体验与之前版本的不同

    RHEL7和CentOS7出来有一段时间了,拿出点时间研究下,有几个地方跟6和5系列相比改变比较大,估计不少童鞋有点不太习惯.下面简要举例说明改变比较大的要点: 一.CentOS的Services使用 ...

  5. Bellman-Ford & SPFA 算法——求解单源点最短路径问题

    Bellman-Ford算法与另一个非常著名的Dijkstra算法一样,用于求解单源点最短路径问题.Bellman-ford算法除了可求解边权均非负的问题外,还可以解决存在负权边的问题(意义是什么,好 ...

  6. 单源最短路径——dijkstra算法

    dijkstra算法与prim算法的区别   1.先说说prim算法的思想: 众所周知,prim算法是一个最小生成树算法,它运用的是贪心原理(在这里不再证明),设置两个点集合,一个集合为要求的生成树的 ...

  7. 一个SQLSERVER触发器的示例

    CREATE TRIGGER WoStateChange on T_PD_WorkOrder AFTER UPDATE AS BEGIN declare @WorkOrderID varchar(20 ...

  8. 【wp之二 页面布局】

    1. panoramic全景视图 新建工程选择:Windows Phone Panorama Application 相当于用放大镜看报纸的感觉.用一张图片作为背景. 2.pivot透视视图

  9. 黄聪:wordpress源码解析-数据库表结构(转)

    如果是一个普通的用户,不需要了解wordpress数据库的结构.但是,如果你正在写一个插件,你应该会对wordpress如何处理它的数据和关系感兴趣.如果你已经尝试使用已经存在的wordpress a ...

  10. Spark作业调度

    Spark在任务提交时,主要存在于Driver和Executor的两个节点. (1)Driver的作用: 用于将所有要处理的RDD的操作转化为DAG,并且根据RDD DAG将JBO分割为多个Stage ...