我编写了一个非常简单的代码,只是为了查看分配器是否可以为字符串分配内存。它有效。但是,我想知道我是否可以使用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;
}
答案 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_ptr和use 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来实际需要使用矢量某处;)