首页 > 解决方案 > C ++我的复制构造函数无法将内存移动到新区域

问题描述

所以我最近开始着手让我自己的向量类工作,我有点卡在我的复制构造函数上。我显然对 c++ 很陌生,希望堆栈溢出方面的好人可以帮助我一点。所以我得到了这个复制构造函数,它复制正在使用的实际 ptr、ptr 的结束索引(用户可以使用的元素)和 ptr 拥有的实际容量/保留内存,加上已用内存的大小。

    vector(const vector &other) : storage(other.storage), capacity(other.capacity), 
        endIndex(other.endIndex), m_size(other.m_size)
    {
        T* storage = new T[capacity];
        memcpy(storage, other.storage, sizeof(T) * capacity);
    }

问题在于,虽然它看似成功地复制了信息,但如果其中一个对象超出范围,则该信息或至少其中一些信息会被删除。如果我还对其中一个矢量对象执行 push_back,则它们都会发生。因此,可以说他们共享 ptr 的地址是相当安全的。例如,如果我在我的主函数中运行此代码

int main()
{

    vector<int> vec;
    vec.push_back(5);
    vec.push_back(55);
    vec.push_back(500);
    vector<int> vec1 = vec;

    for (int i = 0; i < vec1.size(); i++)
    {
        std::cout << vec1[i] << std::endl;
    }
    return 0;
}

我会收到此错误消息

5
55
500
free(): double free detected in tcache 2
Aborted (core dumped)

我假设这是因为 ptr 在循环过程中被删除,它反过来以一种很好的方式使程序崩溃。push_back 的另一个例子是

int main()
{

    vector<int> vec;
    vec.push_back(5);
    vec.push_back(55);
    vec.push_back(500);
    vector<int> vec1 = vec;
    vec.push_back(55);

    for (int i = 0; i < vec1.size() + 1; i++)
    {
        std::cout << vec1[i] << std::endl;
    }
    return 0;
}

你可以明显看到我实际上是在原始向量对象上 push_back 而不是新向量对象,我什至必须增加 for-loops 范围才能看到新向量上的对象,暗示新向量中的大小整数对象与之前没有变化。这段代码的输出是:

5
55
500
55
free(): double free detected in tcache 2
Aborted (core dumped)

我不希望任何人花时间调试我的代码,我不希望这样。我只是要求一双专业的眼睛扫视它并帮助新手。提前致谢。

标签: c++classtemplatesvectorcopy-constructor

解决方案


您的代码存在多个问题。

第一个也是最重要的一个是:

T* storage = new T[capacity];

那和成员变量storage一样storage。它是一个恰好具有相同名称的局部变量。复制构造函数完成后,除了泄漏内存之外,您什么都没有做。


此外,你有这个:

vector(const vector &other) : storage(other.storage),

这会将指针分配other.storagethis。这实际上是双重免费的来源。您正在做一个浅拷贝,因此当thisother被销毁时,将在调用delete []析构函数时使用相同的指针值。


第三个问题是这样的:

memcpy(storage, other.storage, sizeof(T) * capacity);

这不适用于不可简单复制的类型。假设您解决了除此之外的所有问题。此代码将惨遭失败:

vector<std::string> s;

原因是你不能memcpy用来复制std::string对象,因为std::string它不是简单可复制的。

解决方法是使用std::copy, not memcpy,因为std::copy它(应该)足够聪明,可以memcpy为普通可复制类型执行一个普通循环,或者为非普通可复制类型执行普通循环。


最后一个问题是你对类的命名vector。请注意,std::vector在 C++ 中已经有一个。要么将名称更改为其他名称,要么将您的类放在它自己的命名空间中,这样如果您碰巧在某个地方不会发生名称冲突#include <vector>

将所有这些放在一起,您将拥有这个(未编译,请原谅任何语法错误):


#include <algorithm>

namespace myVector
{
    template <typename T>
    class vector
    {
       private:
            // your member variables
       public:
         //... 
         vector(const vector &other) : capacity(other.capacity), 
                endIndex(other.endIndex), m_size(other.m_size)
        {
          storage = new T[capacity]();
          std::copy(other.storage, other.storage + other.m_size, storage);
        }

        vector& operator=(const vector& other) 
        {
            // see later
        }

        ~vector()
        {
           delete [] storage;
        } 
      //...
  };
}  
     

那么main可能是这样的:

#include <myvector>

int main()
{
    myVector::vector<int> vec;
    vec.push_back(5);
    vec.push_back(55);
    vec.push_back(500);
    myVector::vector<int> vec1 = vec;

    for (int i = 0; i < vec1.size(); i++)
    {
        std::cout << vec1[i] << std::endl;
    }
    return 0;
}

一旦这一切都完成并更正,为了完成规则 3,赋值运算符可以简单地是这样的:

vector& operator=(const vector& other)
{
   if ( &other != this )
   { 
       vector temp(other);
       std::swap(temp.capacity, capacity);
       std::swap(temp.m_size, m_size);
       std::swap(temp.endIndex, endIndex);
       std::swap(temp.storage, storage);
   } 
   return *this;
}

以上是使用复制/交换习语


推荐阅读