动态内存管理类

15 篇文章 506 订阅

非常好的例子,有许多需要注意的地方,所以单独写一篇博客

通过动态内存管理实现vector


#ifndef _VECTOR_H_
#define _VECTOR_H_

#include <memory>
#include <iostream>
#include <string>
#include <map>
#include <initializer_list>
#include <algorithm>


class StrVec
{
    friend bool operator==(const StrVec &lhs, const StrVec &rhs);
    friend bool operator!=(const StrVec &lhs, const StrVec &rhs);
    friend bool operator<(const StrVec &lhs, const StrVec &rhs);
    public:
        StrVec():elements(nullptr), first_free(nullptr), cap(nullptr) { } 
        //使用初始值列表的构造函数,记住initializer_list里面全是常量,且初始值列表含有begin()和end(),可以使用范围for
        StrVec(const std::initializer_list<std::string>&il);
        StrVec(const StrVec &sv);
        StrVec(StrVec &&sv)noexcept; //移动拷贝
        StrVec& operator=(const StrVec &sv);
        StrVec& operator=(StrVec &&sv)noexcept; //移动赋值
        StrVec& operator=(const std::initializer_list<std::string>&il); //重载赋值运算符。
        ~StrVec();
        void push_back(const std::string &s);
        std::size_t size()const { return first_free - elements; }// const StrVec* const this
        std::size_t capacity()const { return cap - elements; }
        std::string* begin()const { return elements; }
        std::string* end()const { return first_free; }
        void reserve(std::size_t n);
        void resize(std::size_t n);

    private:
        //工具函数
        //设置为static很巧妙,只初始化一次,且在对象分配前就初始化,所有对象共用一个。
        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*b, const std::string*e);
        void free();
        void reallocate();
        
        std::string *elements;     //首元素
        std::string *first_free;   //末元素的下一个位置
        std::string *cap;          //分配的内存末尾之后的位置
};


#endif

注意!

1.开始使用了c++11 的nullptr而没有使用NULL,原因是NULL是c风格中的东西,它只是一个宏定义,如果我们用在c++中重载函数有时会出现二义性

所以在c++中我们应该使用nullptr。

foo(int) {}
foo(char *) {}

foo(0)            //调用foo(int)
foo(NULL)    //调用foo(int)
foo(nullptr)   //调用foo(char *)

2.函数参数列表后面添加const一定是类的成员函数,也就是常量成员函数,因为常量成员函数的const是修饰类对象的this指针的。

3.工具函数,在我们定义的类中,如果有多个成员函数有相同的操作,我们就应该定义工具函数,为了避免代码重复,且工具函数应该是private的

4.类中可声明static对象,static是存储在全局区(静态区),简单理解我们可以认为它不属于类(为了理解),类中的static成员可以是该类所有"实例对象"所共用的

,这样就减少了资源的浪费,像例子中的allocator,完全可以所有的对象共用一个alloc,那么我们把它声明为static类型的即可实现,但是要注意static的成员必须在类内

声明,类外定义,一般在main函数的前面我们把所有的static成员初始化。(const static要在类内初始化)。

5.返回一对可使用pair。


#include "vector.h"

void StrVec::push_back(const std::string &s)//必须是const的否则参数为初始值列表的构造函数会出错。
{
    chk_n_alloc();
    alloc.construct(first_free++, s);
}

std::pair<std::string*, std::string*>
StrVec::alloc_n_copy(const std::string *b, const std::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);
    }
}
*/

//不如上面的free好,如果for_each()里面的范围是迭代器,会出现意想不到的问题
void StrVec::free()
{
    if(elements)
    {
        std::for_each(elements, first_free, [](std::string &s) { alloc.destroy(&s); });
        alloc.deallocate(elements, cap - elements);
    }
}

StrVec::StrVec(const StrVec& sv)
{
    auto newdata = alloc_n_copy(sv.begin(), sv.end());
    elements = newdata.first;
    first_free = cap = newdata.second;
}

StrVec::~StrVec()
{
    free();
}

StrVec& StrVec::operator=(const StrVec &sv)
{
    auto data = alloc_n_copy(sv.begin(), sv.end());
    free();
    elements = data.first;
    first_free = cap = data.second;
    return *this;
}

void StrVec::reallocate()
{
    //auto newcapacity = 2 * size();  error
    auto newcapacity = size() ? 2 * size() : 1;
    auto newdata = alloc.allocate(newcapacity);
    auto dest = newdata;
    auto elem = elements;
    //for(; elem != first_free;)      error
    //    *dest++ = *elem++;
    for(std::size_t i = 0; i != size(); ++i)
        alloc.construct(dest++, std::move(*elem++));
    free();
    elements = newdata;
    first_free = dest;
    cap = elements + newcapacity;
}

void StrVec::reserve(std::size_t n)
{
    if(n > capacity())
    {
        auto newcapacity = n;
        auto newdata = alloc.allocate(newcapacity);
        auto dest = newdata;
        auto elem = elements;
        for(std::size_t i = 0; i != size(); ++i)
            alloc.construct(dest++, std::move(*elem++));
        free();
        elements = newdata;
        first_free = dest;
        cap = elements + n;
    }
}

void StrVec::resize(std::size_t n)
{
    std::size_t t = n - size();
    if(n < size())
    {
        while(n--)
        {
            alloc.destroy(--first_free);
        }
    }
    else if(n > size() && n <= capacity())
    {
        for(std::size_t i = 0; i < t; ++i)
        {
            alloc.construct(first_free+i, " ");
        }
    }
    else if(n > capacity())
    {
        auto newcapacity = n;
        auto newdata = alloc.allocate(newcapacity);
        auto dest = newdata;
        auto elem = elements;
        for(std::size_t i = 0; i != size(); ++i)
            alloc.construct(dest++, std::move(*elem++));
        for(std::size_t i = size(); i <= capacity(); ++i)
            alloc.construct(dest++, " ");
        free();
        elements = newdata;
        first_free = newdata+n;
        cap = newdata+n;
    }
}

StrVec::StrVec(const std::initializer_list<std::string>&il):
    elements(nullptr), first_free(nullptr), cap(nullptr)
{

    for(const std::string p : il)
    {
        push_back(p);
    }
}

StrVec::StrVec(StrVec &&sv)noexcept
{
    elements = sv.elements;
    first_free = sv.first_free;
    cap = sv.cap;
    sv.elements = sv.first_free = sv.cap = nullptr;   //确保移动源处于可销毁状态
}

StrVec& StrVec::operator=(StrVec &&sv)noexcept
{
    if(this != &sv) //判断是否自赋值
    {
        free();
        elements = sv.elements;
        first_free = sv.first_free;
        cap = sv.cap;
        sv.elements = sv.first_free = sv.cap = nullptr;
    }
    return *this;
}

StrVec& StrVec::operator=(const std::initializer_list<std::string>&il)
{
    auto data = alloc_n_copy(il.begin(), il.end());    //不需要考虑自赋值
    free();
    elements = data.first;                             //data是pair
    first_free = cap = data.second;
    return *this;
}

bool operator==(const StrVec &lhs, const StrVec &rhs)
{
    return lhs.elements == rhs.elements &&
        lhs.first_free == rhs.first_free &&
        lhs.cap == rhs.cap;
}

bool operator!=(const StrVec &lhs, const StrVec &rhs)
{
    return !(lhs == rhs);
}

bool operator<(const StrVec &lhs, const StrVec &rhs)
{
    auto lhs_address = lhs.elements;
    auto rhs_address = rhs.elements;
    while(lhs_address != lhs.first_free &&  rhs_address != rhs.first_free)
    {
        if(*lhs_address < *rhs_address)
        {
            return true;
        }
        else if(*lhs_address > *rhs_address)
        {
            return false;
        }
        lhs_address++;
        rhs_address++;
    }
    if(lhs_address != lhs.first_free)
    {
        return false;
    }
    else
    {
        return true;
    }
}

注意:部分代码

void StrVec::free()
{
    if(elements)
    {
        //error:std::for_each(elements, first_free, [](std::string *s) { alloc.destroy(s); });
        std::for_each(elements, first_free, [](std::string &s) { alloc.destroy(&s); });
        alloc.deallocate(elements, cap - elements);
    }
}
以前博客里有lambda传递的参数要和处理的对象一致,for_each处理的每一个对象是std::string类型的,所以lambda表达式参数也应该是std::string而不是

std::string*, for_each( )前面的两个指针是用来遍历范围,而不是意味着元素就是std::string*, 分清要处理的类型到底是什么!。



main函数

#include "vector.h"

std::allocator<std::string> StrVec::alloc;

int main()
{
    StrVec sv;
    std::string s;
    while(std::cin >> s && s != "q")
    {
        sv.push_back(s);
    }
    std::cout << "size:" << sv.size() << " ";
    std::cout << "capacity:" << sv.capacity() << " ";
    std::cout << "begin:" <<  *sv.begin() << " ";
    std::cout << "end:" << *(sv.end()-1) << std::endl;
    s = "stop";
    sv.push_back(s);
    sv.push_back(s);
    std::cout << "size:" << sv.size() << " ";
    std::cout << "capacity:" << sv.capacity() << " ";
    std::cout << "begin:" <<  *sv.begin() << " ";
    std::cout << "end:" << *(sv.end()-1) << std::endl;
    sv.reserve(10);
    std::cout << "reserve:" << std::endl;
    std::cout << "size:" << sv.size() << " ";
    std::cout << "capacity:" << sv.capacity() << " ";
    std::cout << "begin:" <<  *sv.begin() << " ";
    std::cout << "end:" << *(sv.end()-1) << std::endl;
    sv.resize(11);    
    std::cout << "resize:" << std::endl;
    std::cout << "size:" << sv.size() << " ";
    std::cout << "capacity:" << sv.capacity() << " ";
    std::cout << "begin:" <<  *sv.begin() << " ";
    std::cout << "end:" << *(sv.end()-1) << std::endl;
    std::cout << "构造函数初始值列表:" << std::endl;
    StrVec sv2({"wang","wei","hao"});
    std::cout << "size:" << sv2.size() << " ";
    std::cout << "capacity:" << sv2.capacity() << " ";
    std::cout << "begin:" <<  *sv2.begin() << " ";
    std::cout << "end:" << *(sv2.end()-1) << std::endl;

    StrVec sv3(sv2);
    StrVec sv4;
    sv4 = sv2;
    std::cout << "拷贝构造:" << std::endl;
    std::cout << "size:" << sv3.size() << " ";
    std::cout << "capacity:" << sv3.capacity() << " ";
    std::cout << "begin:" <<  *sv3.begin() << " ";
    std::cout << "end:" << *(sv3.end()-1) << std::endl;
    std::cout << "赋值构造:" << std::endl;
    std::cout << "size:" << sv4.size() << " ";
    std::cout << "capacity:" << sv4.capacity() << " ";
    std::cout << "begin:" <<  *sv4.begin() << " ";
    std::cout << "end:" << *(sv4.end()-1) << std::endl;
}




新添加重载操作符== , != , <。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏天的技术博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值