C++primer 练习13.39

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() ? 2 * size() : 1;
    auto newdata = alloc.allocate(newcapacity);
    auto dest = newdata;
    auto elem = elements;
    for (size_t i = 0;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(5, "handsome");
    cout << sv.size() << " " << sv.capacity() << endl;
    sv.resize(3);
    cout << sv.size() << " " << sv.capacity() << endl;
    sv.resize(6);
    cout << sv.size() << " " << sv.capacity() << endl;
    sv.reserve(20);
    sv.push_back("handsome");
    cout << sv.size() << " " << sv.capacity() << endl;
    return 0;
}
时间: 2024-10-27 17:04:39

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

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

拷贝控制和资源管理 • 类的行为像一个值.意味着它应该有自己的状态,当我们拷贝一个像值得对象时,副本和原对象是完全独立的,改变副本不会对原对象有任何影响. • 行为像指针的类则共享状态.当我们拷贝一个这种类的对象时,副本和原对象使用相同的底层数据,改变副本也会改变原对象. 13.2节练习 1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 class HasPtr { 6 public: 7 HasP

C++ Primer Plus 13章的疑问

1 void show(const Brass &rba) 2 { 3 rba.ViewAcct(); 4 cout<<endl; 5 } 6 7 void inadequate(Brass ba) 8 { 9 ba.ViewAcct(); 10 cout<<endl; 11 } 12 13 14 BrassPlus buzz("Buzz Parsec", 00001111, 4300); 15 16 show(buzz); 17 inadequate(

函数指针(——C primer 第13章)

函数指针: int  f(int);//声明一个函数 int  (*pf)(int)=&f;//创建一个函数指针,并将函数f的函数地址对它进行初始化. 其实函数名在被使用时,总是由编译器将它转化为指针.上面的那个&那个并非必要,只是显示 的说明编译器将隐式执行的任务. int ans; ans=f(25);//使用名字调用函数f,其实在执行时,函数名f被转化为函数指针,指向函数在内存中的地址. ans=(*pf)(25);//将函数指针转化为函数名,其实在执行时又转化回去了 ans=pf(

高级声明(——C primer第13章)

高级声明: (1) int *f,g: 这个只声明了一个f指针.*是都是紧跟在其后的. (2) int *f(); f是一个函数,它的返回值是一个指向整型的指针. (3) int  (*f)(); f是一个函数指针,这个函数的返回值是一个int类型. (4) int *(*f)(); f是一个函数指针,这个函数的返回值是一个int*类型 (5) int f[]; f是一个整型数组,数组的长度暂时省略 (6) int *f[] f是一个数组,数组的元素是指向整型的指针 (7)  int f()[]

C++primer 练习13.36

#pragma once #include<string> #include<set> using namespace std; class Message { friend class Folder; friend void swap(Message &lhs, Message &rhs); public: explicit Message(const string &str=""):contents(str){} Message(co

C++primer 13.5节练习

练习13.39 1 #include <iostream> 2 #include <string> 3 #include <utility> 4 #include <memory> 5 6 using namespace std; 7 8 class StrVec { 9 public: 10 StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {} 11 StrVec(const

第 13 章

13.1 [出题思路] 理解拷贝构造函数的基本概念. [解答] 如果构造函数的第一个参数是自身类类型的引用,且所有其他参数(如果有的话)都有默认值,则此构造函数是拷贝构造函数.拷贝构造函数在以下几种情况下会被使用: 拷贝初始化(用 = 定义变量). 将一个对象作为实参传递给非引用类型的形参. 一个返回类型为非引用类型的函数返回一个对象. 用花括号列表初始化一个数组中的元素或一个聚合类中的成员. 初始化标准库容器或调用其 insert/push 操作时,容器会对其元素进行拷贝初始化. 13.2 [

《C++primer(第五版)》学习之路-第十三章:拷贝控制

[ 声明:版权所有,转载请标明出处,请勿用于商业用途.  联系信箱:[email protected]] 13.1 拷贝.赋值与销毁 1.当定义一个类时,我们显式地或隐式地指定在此类型的对象拷贝.移动.赋值和销毁时做什么.一个类通过定义五种特殊的成员函数来控制这些操作,包括:拷贝构造函数,拷贝赋值运算符,移动构造函数,移动赋值运算符和析构函数. 2.在一个构造函数中,成员的初始化是在函数体执行之前完成的,且按照它们在类中出现的顺序进行初始化.在一个析构函数中,首先执行函数体,然后销毁成员.成员按

c++primer(第五版) 第十三章 拷贝控制习题答案

纯原创    转载请注明出处:http://blog.csdn.net/axuan_k 13.2    13.3   13.4    13.5 #include<iostream> using namespace std; class Point{ int a; }; Point global; //13.4 Point foo_bar(Point arg) //1 { Point local = arg, *heap = new Point(global); //2,3 *heap = lo