C++ 在动态数组类的实现中发现错误。生成字符串列表后崩溃

C++ 在动态数组类的实现中发现错误。生成字符串列表后崩溃,c++,templates,placement-new,C++,Templates,Placement New,我以前写过一个类似于vector的DynamicCarray类,它很有效。 我还编写了一个演示,其中的性能很差,因为它只有长度和指针,并且每次都要增长。因此,添加n个元素就是O(n^2) 此代码的目的只是演示新的布局。该代码适用于不使用动态内存的类型,但字符串崩溃,-fsanize=address表明addEnd()方法中分配的内存正在用于打印。我注释掉了removeEnd,代码只是添加元素,然后打印它们。我只是没看到虫子。有人能辨别出哪里出了问题吗 #include <iostream&

我以前写过一个类似于vector的DynamicCarray类,它很有效。 我还编写了一个演示,其中的性能很差,因为它只有长度和指针,并且每次都要增长。因此,添加n个元素就是O(n^2)

此代码的目的只是演示新的布局。该代码适用于不使用动态内存的类型,但字符串崩溃,-fsanize=address表明addEnd()方法中分配的内存正在用于打印。我注释掉了removeEnd,代码只是添加元素,然后打印它们。我只是没看到虫子。有人能辨别出哪里出了问题吗

#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;ifriend 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它不可能是可复制的,有用户定义的复制构造函数。但它是可移动的。