c++ - 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)
我不希望任何人花时间调试我的代码,我不希望这样。我只是要求一双专业的眼睛扫视它并帮助新手。提前致谢。
解决方案
您的代码存在多个问题。
第一个也是最重要的一个是:
T* storage = new T[capacity];
那和成员变量storage
不一样storage
。它是一个恰好具有相同名称的局部变量。复制构造函数完成后,除了泄漏内存之外,您什么都没有做。
此外,你有这个:
vector(const vector &other) : storage(other.storage),
这会将指针分配other.storage
给this
。这实际上是双重免费的来源。您正在做一个浅拷贝,因此当this
和other
被销毁时,将在调用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;
}
以上是使用复制/交换习语
推荐阅读
- node.js - 检索 URL 参数
- php - 想通过 HTACCESS 绕过 CDN 域。重写规则需要帮助
- python - 不能通过逻辑回归中的梯度下降来减少损失
- flutter - 从 Firestore 中删除文档中的字段
- reactjs - 2 次 onSubmit 点击后反应组件更新
- javascript - 不知道为什么反应中的这个异步调用有效
- c++ - 如何在 C++ 中将秒更改为 HH:MM:SS 格式?
- environment-variables - CircleCI 将环境变量作为空字符串 - 不拾取环境变量的值
- node.js - Express,app.listen 没有点击回调,也没有错误
- python - 为什么我们使用 else if?