C++ 我的复制构造函数无法将内存移动到新区域
C++ my copy constructor fails to move the memory to a new area
所以我最近开始着手让我自己的向量 class 工作并且我有点卡在我的复制构造函数上。我显然是 c++ 的新手,希望堆栈溢出方面的好人能帮我一些忙。所以我得到了这个复制构造函数,它复制了正在使用的实际 ptr、ptr 的结束索引(用户可以使用的元素)和 ptr 拥有的实际 capacity/reserved 内存,加上已用内存的大小。
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)
我不希望任何人抽出时间来调试我的代码,我不希望这样。我只是要求一双专业的眼睛来浏览它并帮助新手。提前致谢。
您的代码存在多个问题。
第一个也是最重要的是:
T* storage = new T[capacity];
那个storage
与成员变量storage
不一样。它是一个局部变量,恰好具有相同的名称。复制构造函数完成后,除了泄漏内存,你什么都没做。
另外,你还有这个:
vector(const vector &other) : storage(other.storage),
这会将指针 other.storage
分配给 this
。这实际上是 double-free 的来源。您正在执行浅拷贝,因此当 this
和 other
被销毁时,将在析构函数中调用 delete []
时使用相同的指针值。
第三期是这样的:
memcpy(storage, other.storage, sizeof(T) * capacity);
这不适用于不可平凡复制的类型。假设您解决了除此问题之外的所有问题。这段代码会失败得很惨:
vector<std::string> s;
原因是您不能使用 memcpy
复制 std::string
对象,因为 std::string
不可简单复制。
解决方法是使用 std::copy
,而不是 memcpy
,因为 std::copy
是(应该)足够聪明,可以为平凡可复制的类型执行 memcpy
,或用于非平凡可复制类型的普通循环。
最后一期是你对classvector
的命名。请注意,C++ 中已经有一个 std::vector
。要么将名称更改为其他名称,要么将您的 class 放在它自己的命名空间中,这样如果您碰巧在某个地方 #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;
}
以上是使用copy/swap idiom
问题很简单,但很难在您自己的代码中看到,因为您知道自己想要它做什么。您可以通过在调试器中逐步执行并在每一行上仔细检查 storage
的值 和地址 来追踪它。
说真的,先试试看。
好的,就是这样:
vector(const vector &other)
: storage(other.storage) // 1. copy the pointer, so this->storage is shared
, capacity(other.capacity)
, endIndex(other.endIndex)
, m_size(other.m_size)
{
// 2. declare a local variable called storage which shadows this->storage
T* storage = new T[capacity];
memcpy(storage, other.storage, sizeof(T) * capacity);
}
您不希望共享存储,因此您不应该初始化storage(other.storage)
。实际上没有理由在您的代码中执行此操作。如果你只是将它初始化为 nullptr
你会很快意识到构造函数主体中的局部变量是错误的。
只需从 T* storage = ...
中删除 T*
即可解决您眼前的问题。关于使用 std::copy
而不是 memcpy
以及如何更好地构建代码的所有其他建议都是很好的建议,您也应该这样做。
所以我最近开始着手让我自己的向量 class 工作并且我有点卡在我的复制构造函数上。我显然是 c++ 的新手,希望堆栈溢出方面的好人能帮我一些忙。所以我得到了这个复制构造函数,它复制了正在使用的实际 ptr、ptr 的结束索引(用户可以使用的元素)和 ptr 拥有的实际 capacity/reserved 内存,加上已用内存的大小。
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)
我不希望任何人抽出时间来调试我的代码,我不希望这样。我只是要求一双专业的眼睛来浏览它并帮助新手。提前致谢。
您的代码存在多个问题。
第一个也是最重要的是:
T* storage = new T[capacity];
那个storage
与成员变量storage
不一样。它是一个局部变量,恰好具有相同的名称。复制构造函数完成后,除了泄漏内存,你什么都没做。
另外,你还有这个:
vector(const vector &other) : storage(other.storage),
这会将指针 other.storage
分配给 this
。这实际上是 double-free 的来源。您正在执行浅拷贝,因此当 this
和 other
被销毁时,将在析构函数中调用 delete []
时使用相同的指针值。
第三期是这样的:
memcpy(storage, other.storage, sizeof(T) * capacity);
这不适用于不可平凡复制的类型。假设您解决了除此问题之外的所有问题。这段代码会失败得很惨:
vector<std::string> s;
原因是您不能使用 memcpy
复制 std::string
对象,因为 std::string
不可简单复制。
解决方法是使用 std::copy
,而不是 memcpy
,因为 std::copy
是(应该)足够聪明,可以为平凡可复制的类型执行 memcpy
,或用于非平凡可复制类型的普通循环。
最后一期是你对classvector
的命名。请注意,C++ 中已经有一个 std::vector
。要么将名称更改为其他名称,要么将您的 class 放在它自己的命名空间中,这样如果您碰巧在某个地方 #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;
}
以上是使用copy/swap idiom
问题很简单,但很难在您自己的代码中看到,因为您知道自己想要它做什么。您可以通过在调试器中逐步执行并在每一行上仔细检查 storage
的值 和地址 来追踪它。
说真的,先试试看。
好的,就是这样:
vector(const vector &other)
: storage(other.storage) // 1. copy the pointer, so this->storage is shared
, capacity(other.capacity)
, endIndex(other.endIndex)
, m_size(other.m_size)
{
// 2. declare a local variable called storage which shadows this->storage
T* storage = new T[capacity];
memcpy(storage, other.storage, sizeof(T) * capacity);
}
您不希望共享存储,因此您不应该初始化storage(other.storage)
。实际上没有理由在您的代码中执行此操作。如果你只是将它初始化为 nullptr
你会很快意识到构造函数主体中的局部变量是错误的。
只需从 T* storage = ...
中删除 T*
即可解决您眼前的问题。关于使用 std::copy
而不是 memcpy
以及如何更好地构建代码的所有其他建议都是很好的建议,您也应该这样做。