运算符重载内存泄漏
operator overloading memory leak
最近我有一个任务要用 C++ 完成,用联合、交集等作为重载运算符实现一个集合 class。我在重载 operator+() 时遇到了问题。我决定使用向量并利用一些算法的库函数。问题是我必须将数组指针和数组大小传递给构造函数。这让这个任务有点复杂......我可以编译它但是在 "z=a+b" 操作期间我遇到了某种内存泄漏。谁能解释我做错了什么?
class Set {
int number; // array size (can't be changed)
int *elems; // array pointer (same)
public:
Set();
Set(int, int*); // (can't be changed)
~Set();
friend Set operator+(const Set& X,const Set& Y){
std::vector<int> v(X.number+Y.number);
std::vector<int>::iterator it;
it=std::set_union (X.elems, X.elems+X.number, Y.elems, Y.elems+Y.number, v.begin());
v.resize(it-v.begin());
Set Z;
Z.number=v.size();
Z.elems=&v[0];
return Z;
}
};
Set::Set(){};
Set::Set(int n, int* array){
number=n;
elems = array = new int[number];
for(int i=0; i<number; i++) // creating Set
std::cin >> elems[i];
std::sort(elems, elems + number);
}
Set::~Set(){
delete[] elems;
}
int main(){
int* pointer;
Set z;
Set a = Set(5, pointer);
Set b = Set(2, pointer);
z=a+b;
}
我添加了复制构造函数和复制赋值,按照 NathanOliver 的建议更改了 operator+(),现在我正在传递给构造函数静态数组。仍然有内存泄漏,奇怪的是,即使在 main 中只有 class 变量初始化,我也有这个内存泄漏,不管是否有参数都没有关系......有什么建议吗?我认为 cunstructor 是有效的。
Set::Set(int n, int* array){
number = n;
elems = array;
std::sort(elems, elems + number);
}
Set::Set(const Set& s){
number=s.number;
elems=s.elems;
}
Set& operator=(const Set& X){
if(this==&X)
return *this;
delete [] elems;
elems=X.elems;
number=X.number;
return *this;
我使用 gcc (tdm64-2) 4.8.1 编译器。
最好的解决方案(但我不知道你是否被允许专门这样做)是在你的 Set
内部使用 vector
并从传入的指针分配它并且使用双迭代器构造函数的长度。
现在,如果这不可能,您需要正确地管理您的 class:
的内存
- 你需要一个拷贝构造函数和拷贝赋值运算符。
- 在你的
operator+
中你不能创建一个本地向量然后获取它的内存地址,一旦运算符 returns. 该内存就会消失
- 可能还有其他我没听清楚的东西。
在
friend Set operator+(const Set& X,const Set& Y){
std::vector<int> v(X.number+Y.number);
std::vector<int>::iterator it;
it=std::set_union (X.elems, X.elems+X.number, Y.elems, Y.elems+Y.number, v.begin());
v.resize(it-v.begin());
Set Z;
Z.number=v.size();
Z.elems=&v[0];
return Z;
}
您创建一个矢量,对其进行修改,然后将 elems
设置为指向该矢量包含的内容。问题是当向量在函数结束时被销毁时,向量持有的内存被释放。所以你现在有一个指向你不再拥有的内存的指针。试图用它做任何事情都是未定义的行为。你可以做的是创建一个新数组,将 vector
的元素复制到数组中,然后将新数组分配给 `elems
Set Z;
Z.number= v.size();
Z.elems= new int[z.number];
for (int i = 0; i < Z.number; i++)
Z.elems[i] = v[i];
return Z;
其次,您需要为您定义一个复制构造函数和赋值运算符class。要做到这一点参考:What is The Rule of Three?
当您有 z=a+b
时,使用 赋值运算符 Set
class。您没有定义此运算符的自定义版本,因此使用默认的 compiler-generated 版本。这个编译器生成的赋值 operator=()
只是做了一个 成员明智的复制 .
由于您的 Set
class 中有 原始拥有指针 ,因此无法正常工作:编译器生成的默认 operator=()
浅拷贝指针,相反你应该深拷贝数据。
解决此问题的一个方法是定义您自己的 operator=()
版本,注意对源数据进行适当的 深度复制 。
请注意,在这种情况下,您还应该定义一个复制构造函数。
但更好的选择是摆脱拥有原始指针数据成员,而是使用 RAII 构建块 class,例如 std::vector
.
因此,例如,而不是这些数据成员:
int number; // array size (can't be changed)
int *elems; // array pointer (same)
你可以只有一个:
std::vector<int> elems;
如果这样做,编译器生成的默认 operator=()
将正常工作,因为它将复制 std::vector
数据成员(不是原始拥有指针),并且 std::vector
知道如何在不泄漏资源的情况下正确复制其内容。
最近我有一个任务要用 C++ 完成,用联合、交集等作为重载运算符实现一个集合 class。我在重载 operator+() 时遇到了问题。我决定使用向量并利用一些算法的库函数。问题是我必须将数组指针和数组大小传递给构造函数。这让这个任务有点复杂......我可以编译它但是在 "z=a+b" 操作期间我遇到了某种内存泄漏。谁能解释我做错了什么?
class Set {
int number; // array size (can't be changed)
int *elems; // array pointer (same)
public:
Set();
Set(int, int*); // (can't be changed)
~Set();
friend Set operator+(const Set& X,const Set& Y){
std::vector<int> v(X.number+Y.number);
std::vector<int>::iterator it;
it=std::set_union (X.elems, X.elems+X.number, Y.elems, Y.elems+Y.number, v.begin());
v.resize(it-v.begin());
Set Z;
Z.number=v.size();
Z.elems=&v[0];
return Z;
}
};
Set::Set(){};
Set::Set(int n, int* array){
number=n;
elems = array = new int[number];
for(int i=0; i<number; i++) // creating Set
std::cin >> elems[i];
std::sort(elems, elems + number);
}
Set::~Set(){
delete[] elems;
}
int main(){
int* pointer;
Set z;
Set a = Set(5, pointer);
Set b = Set(2, pointer);
z=a+b;
}
我添加了复制构造函数和复制赋值,按照 NathanOliver 的建议更改了 operator+(),现在我正在传递给构造函数静态数组。仍然有内存泄漏,奇怪的是,即使在 main 中只有 class 变量初始化,我也有这个内存泄漏,不管是否有参数都没有关系......有什么建议吗?我认为 cunstructor 是有效的。
Set::Set(int n, int* array){
number = n;
elems = array;
std::sort(elems, elems + number);
}
Set::Set(const Set& s){
number=s.number;
elems=s.elems;
}
Set& operator=(const Set& X){
if(this==&X)
return *this;
delete [] elems;
elems=X.elems;
number=X.number;
return *this;
我使用 gcc (tdm64-2) 4.8.1 编译器。
最好的解决方案(但我不知道你是否被允许专门这样做)是在你的 Set
内部使用 vector
并从传入的指针分配它并且使用双迭代器构造函数的长度。
现在,如果这不可能,您需要正确地管理您的 class:
的内存- 你需要一个拷贝构造函数和拷贝赋值运算符。
- 在你的
operator+
中你不能创建一个本地向量然后获取它的内存地址,一旦运算符 returns. 该内存就会消失
- 可能还有其他我没听清楚的东西。
在
friend Set operator+(const Set& X,const Set& Y){
std::vector<int> v(X.number+Y.number);
std::vector<int>::iterator it;
it=std::set_union (X.elems, X.elems+X.number, Y.elems, Y.elems+Y.number, v.begin());
v.resize(it-v.begin());
Set Z;
Z.number=v.size();
Z.elems=&v[0];
return Z;
}
您创建一个矢量,对其进行修改,然后将 elems
设置为指向该矢量包含的内容。问题是当向量在函数结束时被销毁时,向量持有的内存被释放。所以你现在有一个指向你不再拥有的内存的指针。试图用它做任何事情都是未定义的行为。你可以做的是创建一个新数组,将 vector
的元素复制到数组中,然后将新数组分配给 `elems
Set Z;
Z.number= v.size();
Z.elems= new int[z.number];
for (int i = 0; i < Z.number; i++)
Z.elems[i] = v[i];
return Z;
其次,您需要为您定义一个复制构造函数和赋值运算符class。要做到这一点参考:What is The Rule of Three?
当您有 z=a+b
时,使用 赋值运算符 Set
class。您没有定义此运算符的自定义版本,因此使用默认的 compiler-generated 版本。这个编译器生成的赋值 operator=()
只是做了一个 成员明智的复制 .
由于您的 Set
class 中有 原始拥有指针 ,因此无法正常工作:编译器生成的默认 operator=()
浅拷贝指针,相反你应该深拷贝数据。
解决此问题的一个方法是定义您自己的 operator=()
版本,注意对源数据进行适当的 深度复制 。
请注意,在这种情况下,您还应该定义一个复制构造函数。
但更好的选择是摆脱拥有原始指针数据成员,而是使用 RAII 构建块 class,例如 std::vector
.
因此,例如,而不是这些数据成员:
int number; // array size (can't be changed) int *elems; // array pointer (same)
你可以只有一个:
std::vector<int> elems;
如果这样做,编译器生成的默认 operator=()
将正常工作,因为它将复制 std::vector
数据成员(不是原始拥有指针),并且 std::vector
知道如何在不泄漏资源的情况下正确复制其内容。