C++ 在动态数组类的实现中发现错误。生成字符串列表后崩溃
我以前写过一个类似于vector的DynamicCarray类,它很有效。 我还编写了一个演示,其中的性能很差,因为它只有长度和指针,并且每次都要增长。因此,添加n个元素就是O(n^2) 此代码的目的只是演示新的布局。该代码适用于不使用动态内存的类型,但字符串崩溃,-fsanize=address表明addEnd()方法中分配的内存正在用于打印。我注释掉了removeEnd,代码只是添加元素,然后打印它们。我只是没看到虫子。有人能辨别出哪里出了问题吗C++ 在动态数组类的实现中发现错误。生成字符串列表后崩溃,c++,templates,placement-new,C++,Templates,Placement New,我以前写过一个类似于vector的DynamicCarray类,它很有效。 我还编写了一个演示,其中的性能很差,因为它只有长度和指针,并且每次都要增长。因此,添加n个元素就是O(n^2) 此代码的目的只是演示新的布局。该代码适用于不使用动态内存的类型,但字符串崩溃,-fsanize=address表明addEnd()方法中分配的内存正在用于打印。我注释掉了removeEnd,代码只是添加元素,然后打印它们。我只是没看到虫子。有人能辨别出哪里出了问题吗 #include <iostream&
#include <iostream>
#include <string>
#include <memory.h>
using namespace std;
template<typename T>
class BadGrowArray {
private:
uint32_t size;
T* data;
public:
BadGrowArray() : size(0), data(nullptr) {}
~BadGrowArray() {
for (uint32_t i = 0; i < size; i++)
data[i].~T();
delete [] (char*)data;
}
BadGrowArray(const BadGrowArray& orig) : size(orig.size), data((T*)new char[orig.size*sizeof(T)]) {
for (int i = 0; i < size; i++)
new (data + i) T(orig.data[i]);
}
BadGrowArray& operator =(BadGrowArray copy) {
size = copy.size;
swap(data, copy.data);
return *this;
}
void* operator new(size_t sz, void* p) {
return p;
}
void addEnd(const T& v) {
char* old = (char*)data;
data = (T*)new char[(size+1)*sizeof(T)];
memcpy(data, old, size*sizeof(T));
new (data+size) T(v); // call copy constructor placing object at data[size]
size++;
delete [] (char*)old;
}
void removeEnd() {
const char* old = (char*)data;
size--;
data[size].~T();
data = (T*)new char[size*sizeof(T)];
memcpy(data, old, size*sizeof(T));
delete [] (char*)old;
}
friend ostream& operator <<(ostream& s, const BadGrowArray& list) {
for (int i = 0; i < list.size; i++)
s << list.data[i] << ' ';
return s;
}
};
class Elephant {
private:
string name;
public:
Elephant() : name("Fred") {}
Elephant(const string& name) {}
};
int main() {
BadGrowArray<int> a;
for (int i = 0; i < 10; i++)
a.addEnd(i);
for (int i = 0; i < 9; i++)
a.removeEnd();
// should have 0
cout << a << '\n';
BadGrowArray<string> b;
b.addEnd("hello");
string s[] = { "test", "this", "now" };
for (int i = 0; i < sizeof(s)/sizeof(string); i++)
b.addEnd(s[i]);
// b.removeEnd();
cout << b << '\n';
BadGrowArray<string> c = b; // test copy constructor
c.removeEnd();
c = b; // test operator =
}
#包括
#包括
#包括
使用名称空间std;
模板
类数组{
私人:
uint32_t尺寸;
T*数据;
公众:
BadGrowArray():大小(0),数据(nullptr){}
~BadGrowArray(){
对于(uint32_t i=0;i friend ostream&operator使用memcpy
仅对可复制的类型有效
编译器甚至可能会警告您,例如:
warning: memcpy(data, old, size * sizeof(T));
writing to an object of non-trivially copyable type 'class string'
use copy-assignment or copy-initialization instead [-Wclass-memaccess]
请注意,您的代码不会移动对象,而是移动memcpy
对象,这意味着如果它们具有指向对象内部位置的内部指针,则您的mem复制对象仍将指向旧位置
类型不会有指向对象本身某个位置的内部指针(或可能阻止mem复制的类似问题),否则类型必须在复制时处理这些指针,并实现适当的复制和赋值操作,这将使其非常容易复制
要修复addEnd
方法以进行正确复制,对于非普通可复制类型,如果使用C++17,则可以向代码中添加if constexpr
,如下所示:
if constexpr(std::is_trivially_copyable_v<T>) {
memcpy(data, old, size * sizeof(T));
}
else {
for(std::size_t i = 0; i < size; ++i) {
new (data + i) T(std::move_if_noexcept(old[i]));
}
}
如果constexpr(std::是可复制的){
memcpy(数据,旧的,尺寸*尺寸(T));
}
否则{
对于(标准::大小\u t i=0;i
如果您使用的是C++14或更早版本,则需要使用SFINAE进行两个版本的复制
请注意,代码的其他部分也可能需要一些修复。使用memcpy
仅对可复制的类型有效。请参阅:既然我没有复制对象,为什么不可以移动对象?添加了一个详细的答案,数据=(T*)malloc((大小+1)*sizeof(T));
看起来比数据=(T*)新字符[(大小+1)*sizeof(T)]更干净
在我看来。谢谢,问题的关键是如何在不使用复制构造函数的情况下实现同等的效率,并且您已经演示了如何触发移动。我很震惊字符串不是可复制的。@Dov它不可能是可复制的,有用户定义的复制构造函数。但它是可移动的。