emplace_back 和 push_back 给出 'double free or corruption (fasttop)' 错误,尽管定义了复制和移动构造函数
emplace_back and push_back give 'double free or corruption (fasttop)' error although copy and move constructor are defined
我刚刚接触 C++,可能遗漏了一些明显的东西。我有一个动态分配数组的 class,我想将其对象放入一个向量中。由于必须在析构函数中释放数组并由 vector.push_back()
调用,我读到我必须定义一个复制和移动构造函数以避免内存损坏错误。但是,下面的示例仍然给我一个 double free or corruption (fasttop)
错误。
#include <iostream>
#include <vector>
using namespace std;
class bla {
public:
int* arr;
bla() {
arr = new int[10];
for (size_t i = 0; i < 10; i++) {
arr[i] = 42;
}
}
bla(bla&& b) : arr(b.arr) {
cout << "move" << endl;
}
bla(const bla& b) : arr(b.arr) {
cout << "copy" << endl;
}
~bla() {
cout << "delete" << endl;
delete[] arr;
}
};
int main() {
vector<bla> blas;
blas.reserve(5000);
blas.push_back(bla()); // same result with emplace_back
blas.push_back(bla()); // same result with emplace_back
blas.push_back(bla()); // same result with emplace_back
return 0;
}
谁能解释一下我做错了什么,也许还可以提供一个示例,说明如何将具有动态内存的对象添加到向量中(我知道我可以使用指针,但从我读到的内容来看,它也应该适用于对象本身)。
我运行g++ (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
和-Wall -std=c++11 -lm
更新 1:
运行 g++ 7.4.0
上具有相同标志的相同代码不会导致双重释放错误。有人知道为什么吗?
更新 2:
对于后代来说,显然没有那么多的最小示例为浮动的动态数组实现 5 规则。这是你应该做的(如果我错了,请纠正我):
#include <iostream>
#include <vector>
using namespace std;
class bla {
public:
int* arr;
bla()
: arr(nullptr)
{
arr = new int[10];
for (size_t i = 0; i < 10; i++) {
arr[i] = 42;
}
}
~bla() {
cout << "delete" << endl;
delete[] arr;
}
// deep copy constructor
bla(const bla& other) {
cout << "copy" << endl;
arr = new int[10];
for (size_t i = 0; i < 10; i++) {
arr[i] = other.arr[i];
}
}
// deep copy assignment
bla& operator = (const bla& other) {
cout << "copy assignment" << endl;
if (&other != this) {
delete[] arr;
arr = new int[10];
for (size_t i = 0; i < 10; i++) {
arr[i] = other.arr[i];
}
}
return *this;
}
// move constructor
bla(bla&& other)
: arr(other.arr) {
cout << "move" << endl;
other.arr = nullptr;
}
// move assignment
bla& operator = (bla&& other){
cout << "move assignment" << endl;
if(&other != this) {
delete[] arr;
arr = other.arr;
other.arr = nullptr;
}
return *this;
}
};
int main() {
vector<bla> blas;
cout << "=========\nstart pushs\n=========" << endl;
blas.push_back(bla());
blas.push_back(bla());
blas.push_back(bla());
cout << endl << "=========\nend pushs\n=========" << endl << endl;;
cout << "for each:" << endl;
for (bla b : blas) {
cout << b.arr[0] << endl;
}
cout << endl;
cout << "native for:" << endl;
for (size_t i = 0; i < 3; i++) {
cout << blas[i].arr[0] << endl;
}
return 0;
}
I read that I have to define a copy- as well as move-constructor to
avoid memory corruption errors.
的确如此,但他们也需要做正确的事。不只是任何 copy/move 构造函数都能完成这项工作;这里的复制构造函数和移动构造函数都有同样的缺陷。以拷贝构造函数为例:
bla(const bla& b) : arr(b.arr) {
所以,如果你用一张纸和一支笔算出这里发生了什么,在复制构造发生后,新对象和这个原始对象 b
将具有相同的 arr
指针。因为这正是这段代码的作用。
因此,当两个对象都被销毁时,它们的析构函数将尝试 delete[]
完全相同的指针。 b
和同一个对象都有相同的arr
。有你的双免。
移动构造函数出现同样的问题。
此外,显示的 class 没有显式赋值或移动赋值运算符。因此,将这些对象之一分配给另一个对象将:1) 最终泄漏被覆盖对象的 arr
,2) 最终两个对象具有相同的 arr
,并出现相同的问题,当它们得到最终被摧毁。
在复制构造函数的情况下,除了制作复制自 arr
及其值的副本外,您别无选择。在移动构造函数的情况下,您确实可以选择将 b.arr
设置为 nullptr
。然后析构函数将 quietly ignore delete[]
ion of a nullptr
;虽然通常认为在 delete[]
之前显式检查 nullptr
是一种很好的做法。
您还需要实施适当的 operator=
重载(只是一个带有可选移动赋值的常规赋值),以解决同样的问题。
我刚刚接触 C++,可能遗漏了一些明显的东西。我有一个动态分配数组的 class,我想将其对象放入一个向量中。由于必须在析构函数中释放数组并由 vector.push_back()
调用,我读到我必须定义一个复制和移动构造函数以避免内存损坏错误。但是,下面的示例仍然给我一个 double free or corruption (fasttop)
错误。
#include <iostream>
#include <vector>
using namespace std;
class bla {
public:
int* arr;
bla() {
arr = new int[10];
for (size_t i = 0; i < 10; i++) {
arr[i] = 42;
}
}
bla(bla&& b) : arr(b.arr) {
cout << "move" << endl;
}
bla(const bla& b) : arr(b.arr) {
cout << "copy" << endl;
}
~bla() {
cout << "delete" << endl;
delete[] arr;
}
};
int main() {
vector<bla> blas;
blas.reserve(5000);
blas.push_back(bla()); // same result with emplace_back
blas.push_back(bla()); // same result with emplace_back
blas.push_back(bla()); // same result with emplace_back
return 0;
}
谁能解释一下我做错了什么,也许还可以提供一个示例,说明如何将具有动态内存的对象添加到向量中(我知道我可以使用指针,但从我读到的内容来看,它也应该适用于对象本身)。
我运行g++ (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
和-Wall -std=c++11 -lm
更新 1:
运行 g++ 7.4.0
上具有相同标志的相同代码不会导致双重释放错误。有人知道为什么吗?
更新 2: 对于后代来说,显然没有那么多的最小示例为浮动的动态数组实现 5 规则。这是你应该做的(如果我错了,请纠正我):
#include <iostream>
#include <vector>
using namespace std;
class bla {
public:
int* arr;
bla()
: arr(nullptr)
{
arr = new int[10];
for (size_t i = 0; i < 10; i++) {
arr[i] = 42;
}
}
~bla() {
cout << "delete" << endl;
delete[] arr;
}
// deep copy constructor
bla(const bla& other) {
cout << "copy" << endl;
arr = new int[10];
for (size_t i = 0; i < 10; i++) {
arr[i] = other.arr[i];
}
}
// deep copy assignment
bla& operator = (const bla& other) {
cout << "copy assignment" << endl;
if (&other != this) {
delete[] arr;
arr = new int[10];
for (size_t i = 0; i < 10; i++) {
arr[i] = other.arr[i];
}
}
return *this;
}
// move constructor
bla(bla&& other)
: arr(other.arr) {
cout << "move" << endl;
other.arr = nullptr;
}
// move assignment
bla& operator = (bla&& other){
cout << "move assignment" << endl;
if(&other != this) {
delete[] arr;
arr = other.arr;
other.arr = nullptr;
}
return *this;
}
};
int main() {
vector<bla> blas;
cout << "=========\nstart pushs\n=========" << endl;
blas.push_back(bla());
blas.push_back(bla());
blas.push_back(bla());
cout << endl << "=========\nend pushs\n=========" << endl << endl;;
cout << "for each:" << endl;
for (bla b : blas) {
cout << b.arr[0] << endl;
}
cout << endl;
cout << "native for:" << endl;
for (size_t i = 0; i < 3; i++) {
cout << blas[i].arr[0] << endl;
}
return 0;
}
I read that I have to define a copy- as well as move-constructor to avoid memory corruption errors.
的确如此,但他们也需要做正确的事。不只是任何 copy/move 构造函数都能完成这项工作;这里的复制构造函数和移动构造函数都有同样的缺陷。以拷贝构造函数为例:
bla(const bla& b) : arr(b.arr) {
所以,如果你用一张纸和一支笔算出这里发生了什么,在复制构造发生后,新对象和这个原始对象 b
将具有相同的 arr
指针。因为这正是这段代码的作用。
因此,当两个对象都被销毁时,它们的析构函数将尝试 delete[]
完全相同的指针。 b
和同一个对象都有相同的arr
。有你的双免。
移动构造函数出现同样的问题。
此外,显示的 class 没有显式赋值或移动赋值运算符。因此,将这些对象之一分配给另一个对象将:1) 最终泄漏被覆盖对象的 arr
,2) 最终两个对象具有相同的 arr
,并出现相同的问题,当它们得到最终被摧毁。
在复制构造函数的情况下,除了制作复制自 arr
及其值的副本外,您别无选择。在移动构造函数的情况下,您确实可以选择将 b.arr
设置为 nullptr
。然后析构函数将 quietly ignore delete[]
ion of a nullptr
;虽然通常认为在 delete[]
之前显式检查 nullptr
是一种很好的做法。
您还需要实施适当的 operator=
重载(只是一个带有可选移动赋值的常规赋值),以解决同样的问题。