如何使用新字符串而不是分配器创建自己的字符串类型向量?

How could I create my own string-type vector with new string instead of allocator?

本文关键字:字符串 自己的 创建 类型 分配器 向量 何使用      更新时间:2023-10-16

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

#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;
}

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

在回答您的问题之前,我必须指出您的实现存在一些问题。所以让我们走一走!

  • 销毁MyVectorstd::string* a不会被释放

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

    因此,我们必须添加如下方法:

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

  • 如果this->allocate()无法分配,并在您做push_backstd::bad_alloc怎么办

    size已递增,容量已更新,但您尝试推送的字符串尚未复制,并且缓冲区尚未增长,这使您的容器处于无效状态。您最终可能会访问缓冲区边界之外的内存,这是未定义的行为(这可能会起作用,崩溃,甚至让您的公猫怀孕)

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

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

    好吧,它会把std::bad_alloc扔到循环的中间。而且您的temp缓冲区永远不会被解除分配。哎哟。

    这越来越严重了。我们可以在那里放一个 catch 子句,但这开始是很多代码,只是为了保持指针处于良好状态。

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

    我不是100%确定这一点,但我C++直觉告诉我这是非常冒险的。

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

改用new?也许new[]因为这是一个字符串数组? 好吧,这会更干净,特别是如果您无论如何都要使用默认std::allocator

但是等等,还有更好的!

查看C++核心指南,我们可以看到在P.8下我们不应该泄漏任何资源,建议使用RAII,并寻找"裸新"。基本上,这意味着您应该避免在普通代码中使用new来动态分配资源。相反,这些准则鼓励您使用unique_ptr并使用 make_unique() 构造unique_ptrs拥有的对象

作为参考,这里是unique_ptr关于 cpp 偏好的页面。你也可以在这里阅读更多关于它的信息,或者观看该语言的一位设计者比我在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,当你实际需要在某个地方使用一个向量时;)