如何使用新字符串而不是分配器创建自己的字符串类型向量?
How could I create my own string-type vector with new string instead of allocator?
我写了一个非常简单的代码,只是为了看看分配器是否可以为字符串分配内存。它有效。但是,我想知道我是否可以使用新关键字达到相同的效果。如果可以,哪种方法会更好?我的代码示例如下:
#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()
来执行分配,如果可以的话,哪种方法是最好的。
在回答您的问题之前,我必须指出您的实现存在一些问题。所以让我们走一走!
销毁
MyVector
时std::string* a
不会被释放这是最明显的一个。没有析构函数,您正在手动管理原始指针,因此必须解除分配!现在,当MyVector超出范围时,
a
指向的内存将变得无法访问,并且不会有垃圾收集器来清理它。因此,我们必须添加如下方法:
~MyVector() { str.deallocate(a, capacity); }
如果
this->allocate()
无法分配,并在您做push_back
时扔std::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,当你实际需要在某个地方使用一个向量时;)
- 在 cpp 自己的版本中对字符串进行哈希处理
- 如何为C++映射创建自己的字符串比较对象
- 尝试编写我自己的字符串类在 gcc 中出现异常
- 使用 const char * 与 char * 创建自己的字符串
- 如何用自己的定界符分开字符串
- C++,使用自己的条件搜索文件中的字符串
- 如何使用新字符串而不是分配器创建自己的字符串类型向量?
- 在 C++ 中实现自己的字符串类
- 运算符重载 (+=,=) 用于自己的字符串
- 我可以在"setDefaultButton"中使用我自己的用户定义字符串吗
- 读取和分析文件,将解析的字符串的每个部分分配给其自己的变量
- 我可以在C++中使用 != 和 == 进行字符串比较,而无需编写自己的字符串
- 创建我自己的strlen和子字符串函数
- 使向后的字符串成为自己的字符串
- 编写我自己的字符串类"reference to ‘string’ is ambiguous"但出现编译错误
- 不能在标准向量中插入自己的结构,但是字符串可以
- 如何在C++中将字符串中的元素转换为它自己的字符串?
- C++ - 为学校构建自己的字符串类,无法正确重载<运算符
- 创建了自己的字符串类——重载赋值操作符和析构函数的错误
- 如何拥有自己的没有公共构造函数的字符串类