我在释放内存方面遇到了问题。首先,我去展示我的构造函数,析构函数和私有部分:
第1课:
class VectorDinamico {
private:
int util, tam;
int *vect;
/*Rest of the class*/
}
VectorDinamico::VectorDinamico() {
util = 0;
tam = 10;
vect = new int[10];
}
VectorDinamico::VectorDinamico(const VectorDinamico &v){
vect = new int[v.tam];
for (int i = 0 ; i < v.util; i++) {
vect[i] = v.vect[i];
}
util = v.util;
tam = v.tam;
}
VectorDinamico::~VectorDinamico() {
delete []vect;
}
VectorDinamico& VectorDinamico::operator= (const VectorDinamico &v) {
if (this != &v) {
delete []vect;
vect = new int[v.tam];
for (int i = 0 ; i < v.util; i++) {
vect[i] = v.vect[i];
}
util = v.util;
tam = v.tam;
}
return *this;
}
工作正常(Valgrind:退出时没有使用)
第2课:
class Conjunto {
private:
VectorDinamico datos;
/*Rest of the class*/
}
//Is empty because it uses the VectorDinamico constructor( I think )
Conjunto::Conjunto() {}
Conjunto::Conjunto( const Conjunto &c) {
datos = c.datos; // = Is overloaded in VectorDinamico
}
//Is empty because it uses the VectorDinamico destructor( I think )
Conjunto::~Conjunto() {};
Conjunto& Conjunto::operator=( const Conjunto &c) {
if (this != &c)
datos = c.datos;
return *this;
}
工作正常(Valgrind:退出时没有使用)
第3课:(我认为这是问题所在)
class SetConjuntos{
private:
Conjunto *conjuntos;
int util, tam;
/*Rest of the class*/
}
SetConjuntos::SetConjuntos(){
util = 0;
tam = 10;
conjuntos = new Conjunto[10];
}
SetConjuntos::SetConjuntos(const SetConjuntos &sc){
util = sc.util;
tam = sc.tam;
conjuntos = new Conjunto[tam];
for(int i = 0 ; i < util ; i++)
conjuntos[i]=sc.conjuntos[i];
}
//Here is my problem ( I think )
//anyway I've left it empty, because I think it uses the Conjunto destructor's
SetConjuntos::~SetConjuntos(){
//delete []conjuntos; ( Cause segmetation fault )
}
我认为SetConjuntos析构函数不正确,因为Valgrind输出是:
==6838== LEAK SUMMARY:
==6838== definitely lost: 2,912 bytes in 4 blocks
==6838== indirectly lost: 11,320 bytes in 180 blocks
SetConjuntos的析构函数必须如何?
由于
-----------解决了添加operator = to SetConjuntos ----------------
我没有实现赋值运算符,因为他认为如果他没有明确使用则没有必要。我错了。
现在第3课是:
class SetConjuntos{
private:
Conjunto *conjuntos;
int util, tam;
/*Rest of the class*/
}
SetConjuntos::SetConjuntos(){
util = 0;
tam = 10;
conjuntos = new Conjunto[10];
}
SetConjuntos::SetConjuntos(const SetConjuntos &sc){
util = sc.util;
tam = sc.tam;
conjuntos = new Conjunto[tam];
for(int i = 0 ; i < util ; i++)
conjuntos[i]=sc.conjuntos[i];
}
SetConjuntos::~SetConjuntos(){
delete []conjuntos; //( NO cause segmetation fault )
}
SetConjuntos& SetConjuntos::operator=(const SetConjuntos &sc){
if(this != &sc){
util = sc.util;
tam = sc.tam;
Conjunto *conjuntos_loc = new Conjunto[tam];
for(int i = 0 ; i < util ; i++)
conjuntos_loc[i]=sc.conjuntos[i];
delete[] conjuntos;
conjuntos = conjuntos_loc;
}
return *this;
}
答案 0 :(得分:2)
显示的代码没有明显的错误,因此错误在于您未在问题中出现的内容。
作为一个疯狂的猜测,你(可能是不自觉地)使用复制构造或分配,这会产生所有权问题。
记住规则:要么你没有析构函数,赋值运算符或复制构造函数,要么你可能需要所有这三个。
这个规则非常重要,如果碰巧遇到一个你不需要复制构造函数但你需要一个析构函数(不能想到一个,但它可能存在)的情况,请用一个清楚的文档记录下来评论说这不是你忘记的事情。
答案 1 :(得分:1)
我只想向您指出,有更好的方法来编写复制赋值运算符。我们来看看您的代码:
VectorDinamico& VectorDinamico::operator= (const VectorDinamico &v) {
if (this != &v) {
delete []vect;
/* What happens here is that you delete vect [ ], but what
happens if the next line 'vect = next int [ v.tam ];' throws
an exception?
*/
vect = new int[v.tam];
for (int i = 0 ; i < v.util; i++) {
vect[i] = v.vect[i];
}
util = v.util;
tam = v.tam;
}
return *this;
}
看看The rule of three,在这里您将获得如何编写复制赋值运算符的详细说明。
答案 2 :(得分:1)
你似乎在学习15年前的C ++。
std::vector<int>
而不是自己编写VectorDinamico
如果您无法 1 ,请使用std::unique_ptr<int[]>
代替原始int *vect
。它可以节省你必须编写析构函数。您仍然需要编写副本和副本分配。
编写复制赋值的规范正确方法是首先编写swap
方法或重载,如下所示:
void VectorDinamico::swap(VectorDinamico &other) {
std::swap(util, other.util);
std::swap(tam, other.tam);
std::swap(vect, other.vect);
}
然后编写复制赋值运算符,以便它只重用复制构造函数,swap和析构函数:
VectorDinamico& VectorDinamico::operator=(VectorDinamico const &other) {
VectorDinamico tmp(other);
tmp.swap(*this);
}
除了更简单的代码和更少的重复之外,这个好处是,如果分配失败抛出bad_alloc
,两个原始向量都会处于明确定义的状态(原始代码会使左边的一个被破坏) ) 使用std::unique_ptr
包装其他两个类的原始指针成员,并停止手动编写析构函数
如果你正在编写复制构造函数和复制赋值运算符,你应该考虑编写移动版本
如果您正在运行valgrind并且发现泄漏,它将告诉您该内存的分配位置。只需确保您的构建中有调试符号
如果您不确定是否正在调用析构函数,只需添加一个print语句,然后在调试器中查看或逐步执行。看到像I think this is called here
这样的评论只是告诉我你没有尝试过发现。
最后是您的问题:
//Here is my problem ( I think )
//anyway I've left it empty, because I think it uses the Conjunto destructor's
SetConjuntos::~SetConjuntos(){
//delete []conjuntos; ( Cause segmetation fault )
}
您的conjuntos
是一个原始指针 - 您已在其他地方手动删除它们,这里有什么不同?你应该取消对此删除的评论。
如果您使用此未注释的分段错误,请在gdb下运行它或获取核心转储并查看。还要看看valgrind或glibc是否警告你关于双重释放。
几乎可以肯定的是,您正在删除的阵列成员之一已损坏,找到该损坏的起因是您的真正问题。
答案 3 :(得分:0)
SetConjutos的析构函数很好,就像你拥有它一样:
SetConjuntos::~SetConjuntos(){
delete [] conjuntos;
}
但是如果你有指针成员(在VectorDinamico和SetConjutos中)你需要定义一个复制构造函数和赋值运算符:
VectorDinamico::VectorDinamico(const VectorDinamico &v) :
util(v.util),
tam(v.tam),
vect(new int[10])
{
//copy Content of v.vect to vect
}
VectorDinamico& Operator=(const VectorDinamico &v)
{
//Copy util, tam and the CONTENT of vec
return *this;
}
注意:您还需要为SetConjuntos定义复制构造函数和赋值运算符。
如果您没有复制构造函数和赋值运算符,则可能是您尝试多次释放内存。
正如您在我的复制构造函数中所看到的,我使用的是初始化列表,我也建议使用它。