交换操作

通常定义交换,需要一次拷贝和两次赋值:

Hasptr temp = v1;   //创建v1的一个临时副本
v1 = v2;         //将v2赋值给v1
v2 =temp;       //将保存v1的值赋予v2

拷贝一个类值的 Hasptr 会分配一个新的 string 并将其拷贝到 Hasptr 指向的位置,但是更希望 swap 是交换指针,而不是分配 string 的新副本:

string &temp = v1.ps;
v1.ps = v2.ps;
v2.ps = temp.ps;

可以在类上自定义一个版本的 swap:

class HasPtr{
    friend void swap(HasPtr &lhs,HasPtr &rhs);
}

inline swap(HasPtr&,HasPtr&)
{
    using std::swap;
    swap(lhs.ps,rhs.ps);    //交换指针,而不是string的数据
    swap(lhs.i;rhs.i);  //交换int成员
}

与拷贝控制成员不同, swap 并不是必要的。但是,分配了资源的类,定义 swap 可能是一种很重要的优化手段。

赋值运算符中使用 swap

定义了 swap 的类通常用 swap 来定义它们的赋值运算符,这些运算符使用了一种名为拷贝并交换的技术,这种技术将左侧运算对象与右侧运算对象的一个副本进行交换。

rhs 是按值传递的:

HasPtr& HasPtr::operator=(HasPtr rhs)
{
    //交换左侧运算对象和局部变量rhs的内容
    swap(*this,rhs);
    return *this;   //rhs 被销毁,从而delete了rhs中的指针
}

注意:

使用拷贝和交换的赋值运算符自动就是异常安全的,并且能正确处理自赋值。

动态内存管理类

某些类需要在运行时分配可变大小的内存空间,这种类通常可以使用标准库容器来保存它们的数据。但是,这个策略并不是对每一个类都适用:某些类需要自己进行内存空间分配,这些类一般来说必须定义自己的拷贝控制成员来管理所分配的内存。

实现一个 vector 的简化版本,只用于 string

StrVec 的类设计

使用 allocator 来获得原始内存,由于 allocator 分配的内存是未构造的。所以添加新元素时,用 alloctorconstruct 成员在原始内存中创建对象。

当删除一个元素时,使用 destroy 成员来销毁。

每个 StrVec 有三个指针成员指向其元素所使用的内存:

  • elements,指向分配的内存中的首元素。
  • first_free,指向最后一个实际元素之后的位置。
  • cap,指向分配的内存末尾之后的位置。

交换操作

StrVec 还有一个名为 alloc 的静态成员,其类型为 allocator<string>alloc 成员分配 StrVec 使用的内存。

四个工具函数:

  • alloc_n_copy 会分配内存,并拷贝一个给定范围中的元素。
  • free 会销毁构造元素并释放内存。
  • chk_n_alloc 保证 StrVec 至少有容纳一个新元素的空间,如果没有空间添加新元素,chk_n_alloc 会调用 reallocate 类分配更多的内存。
  • reallocate 在内存用完时为 StrVec 分配新的内存。

StrVec 类定义

class StrVec
{
public:
    StrVec():elements(nullptr),first_free(nullptr),cap(nullptr){}
    StrVec(const StrVec&)
    StrVec& operator = (const StrVec&);
    ~StrVec();
    void push_back(const std::string&);
    size_t size() const {return first_cap - elements;}
    size_t capacity() const {return cap - elements;}
    std::string *begin() const {return elements;}
    std::string *end()const  {return first_free;}

private:
    static std::allocator<std::string> alloc;
    void chk_n_alloc()
    {
        if(size() == capacity())
            reallocate();
    }
    std::pair<std::string*,std::string*> alloc_n_copy(const std::string*,const std::string*);
    void free();
    void reallocate();
    std::string *elements;
    std::string *first_free;
    std::string *cap;
}
void StrVec::push_back(const string& s)
{
    chk_n_alloc();  //确保有空间容纳新的元素
    alloc.construct(first_free++,s);    //在first_free指向的元素中构造s的副本,first_free递增
}

pair<string*,string*>
StrVec::alloc_n_copy()(const std::string* b,const std::string* e)
{
    //分配空间保存给定范围中的元素
    auto data =  alloc.allocate(e - b);
    //初始化并返回一个pair
    return {data,uninitialize_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;
}
上一篇:Django_重装系统后无法使用 sqlite 数据库报错:com.intellij.execution.ExecutionException: Exception in thread "main" java.lang.ClassNotFoundException: org.sqlite.JDBC


下一篇:内存管理