我怎么能用新的字符串而不是分配器创建自己的字符串类型向量?

时间:2018-01-11 20:19:12

标签: c++ vector dynamic-memory-allocation

我编写了一个非常简单的代码,只是为了查看分配器是否可以为字符串分配内存。它有效。但是,我想知道我是否可以使用new关键字达到相同的效果。如果可以的话,哪种方法更好?我的代码示例如下:

#include<iostream>
#include<string>
#include<memory>
class MyVector{
private:
    int size;
    int capacity;
    std::allocator<std::string> str;
    std::string*a;
    void allocate(){
        capacity = (size-1)*2;
        std::string*temp = str.allocate(capacity);
        for(int i=0; i<this->getSize();++i){
            temp[i] = a[i];
        }
        str.deallocate(a, 1);
        a = temp;
    }
public:
    MyVector():size(0), capacity(1), a(str.allocate(1)){};
    int getSize(){return size;};
    int getCapacity(){return capacity;};
    void pushBack(std::string input){
        ++size;
        if(this->getSize()>this->getCapacity()){
            this->allocate();
        }
        a[this->getSize()-1]=input;
    }
    std::string at(int index){
        for(int i=0; i<this->getSize();++i){
            if(i==index){
                return a[i];
            }
        }
        return 0;
    }
};
int main(){
    MyVector v;
    v.pushBack("Sam");
    std::cout<<v.at(0)<<std::endl;
return 0;
}

1 个答案:

答案 0 :(得分:0)

如果我正确理解您的问题,您正在尝试动态分配一堆字符串,并想知道您是否可以使用operator new()执行分配,并且你可以,哪种方法最好

在能够回答您的问题之前,我必须指出您的实施存在一些问题。那么让我们来看看吧!

  • std::string* a MyVector 被销毁时无法取消分配

    这是最明显的一个。没有析构函数,您手动管理原始指针,因此必须取消分配!现在,当MyVector超出范围时,a指向的内存将无法访问,并且没有垃圾收集器来清理它。

    因此我们必须添加这样的方法:

    ~MyVector() { str.deallocate(a, capacity); }

  • 如果 this->allocate() 无法分配,并在您执行> std::bad_alloc 时 strong> push_back

    size已经增加,并且容量已更新,但您尝试推送的字符串尚未复制,缓冲区未增加,这使您的容器处于无效状态。您最终可能会访问缓冲区范围之外的内存,这是未定义的行为(这可能会起作用,崩溃,甚至get your male cat pregnant

    好的,这可以通过在实际分配发生后进行分配来轻松修复。没什么大不了。右

  • 如果尝试将字符串从旧缓冲区复制到新缓冲区时,其中一个不能分配自己的内部缓冲区怎么办?

    好吧,它会在循环中间抛出std::bad_alloc。并且您的temp缓冲区永远不会被释放。哎哟。

    这变得越来越严重。我们可以在其中放置一个catch子句,但这只是为了将指针保持在良好的状态而开始的很多代码。

  • 不是 temp[i] = a[i]; 在未初始化的内存上调用赋值运算符,这会是另一种未定义的行为吗?

    我不是百分之百确定这个,但我的C ++本能告诉我这很冒险。

那么,我如何摆脱这一长串问题?

使用new代替?也许new[]因为这是一个字符串数组? 嗯,这会更干净,特别是如果你打算使用默认的std::allocator

但是等等,还有更好的!

查看C++ Core Guidelines,我们可以在P.8下看到我们不应泄漏任何资源,建议使用RAII,并寻找“裸体新”。基本上,这意味着您应该避免在普通代码中使用new来动态分配资源。相反,指南会鼓励您use unique_ptruse make_unique() to construct objects owned by unique_ptrs

作为参考,这里是unique_ptr's page on cppreference。您还可以阅读更多关于here的信息,或者观看该语言的一位设计师解释我所触及的概念,比YouTube

更好。

遵循这些准则,您的代码可以变得更加现代化。它看起来像这样:

#include<string>
#include<memory>
class MyVector{

private:
    int size;
    int capacity;
    std::unique_ptr<std::string[]> a;
    void allocate(){
        size_t new_capacity = size*2;
        auto temp = std::make_unique<std::string[]>(new_capacity);
        std::copy(a.get(), a.get()+size, temp.get());
        capacity = new_capacity; // We have finished all the operations that could throw!
        std::swap(a, temp); // Because this can't throw
    }
public:
    MyVector():size(0), capacity(1) {}
    // Since unique_ptr<>'s destructor is called automatically
    // we don't need to do it explicitely!
    int getSize(){return size;};
    int getCapacity(){return capacity;};
    void pushBack(std::string input){
        if(this->getSize() == this->getCapacity()){ // We have to change the comparison
            this->allocate();
        }
        a[this->getSize()] = input; // This could throw too!
        ++size;
    }
    std::string at(int index){
        if(index >= size)
            throw std::out_of_range("Trying to access an element past the end in MyVector!");

        return a.get()[index];
    }
};

最后一个注释

这个容器效率仍然很低(2的增长因子不是理论上最好的,虽然我不太了解它),它没有移动语义的概念,它不能被复制,它不能专门用于其他类型(虽然这不会太困难),它没有方便的迭代器用于算法或基于范围的for循环,等等。

这是一个非常好的学习练习,我赞赏你试图通过在StackOverflow上发布你的结果来改进它:)

制作一个生产就绪的容器实际上是很多工作,需要相当深入的硬件知识才能正确,所以作为结论,我建议你坚持使用std :: vector来实际需要使用矢量某处;)