#ifndef DELETE
#define DELETE(var) delete var, var = NULL
#endif
using namespace std;
class Teste {
private:
Teste *_Z;
public:
Teste(){
AnyNum = 5;
_Z = NULL;
}
~Teste(){
if (_Z != NULL)
DELETE(_Z);
}
Teste *Z(){
_Z = new Teste;
return _Z;
}
void Z(Teste *value){
value->AnyNum = 100;
*_Z = *value;
}
int AnyNum;
};
int main(int argc, char *argv[]){
Teste *b = new Teste, *a;
a = b->Z();
cout << "a->AnyNum: " << a->AnyNum << "\n";
b->Z(new Teste);
cout << "a->AnyNum: " << a->AnyNum << "\n";
//wdDELETE(a);
DELETE(b);
return 0;
}
我想知道此代码中是否存在内存泄漏
它工作正常,*a
设置两次,AnyNum
在每个cout <<
上打印不同的数字
但是我想知道在setter(_Z
)之后new Teste
发生了什么,我对指针/引用还没有太多的了解,但是对于逻辑我猜它是换成了新的变量
如果它正在泄漏,无论如何都要完成这个而不必再次设置为_Z
?
因为地址没有改变,只是分配了直接内存
我打算使用*&
而不仅仅是指针,但它会有所不同吗?
答案 0 :(得分:5)
此行存在内存泄漏:
b->Z(new Teste);
因为函数的定义:
void Z(Teste *value){
value->AnyNum = 100;
*_Z = *value;
}
看起来Z没有参数应该是一个getter并且带有参数的setter。我怀疑你打算这样做:
void Z(Teste *value){
value->AnyNum = 100;
_Z = value;
}
(注意第三行)也就是说,将指针“value”赋给指针“_Z”而不是复制指向Z指向的值的值。有了这个,第一个内存泄漏将被解决,但代码仍然有一个,因为_Z可能已经持有一个指针。所以你必须这样做:
void Z(Teste *value){
value->AnyNum = 100;
delete _Z; // you don't have to check for null
_Z = value;
}
如另一条评论所述,真正的解决方案是使用智能指针。对于相同的代码,这是一种更现代的方法:
using namespace std;
class Teste {
private:
boost::shared_ptr<Teste> Z_;
public:
Teste() : AnyNum(5), Z_(NULL)
{ }
boost::shared_ptr<Teste> Z()
{
Z_.reset(new Teste);
return Z_;
}
void Z(boost::shared_ptr<Teste> value)
{
value->AnyNum = 100;
Z_ = value;
}
int AnyNum;
};
int main(int argc, char *argv[]){
boost::shared_ptr<Teste> b = new Teste, a;
a = b->Z();
cout << "a->AnyNum: " << a->AnyNum << "\n";
b->Z(boost::shared_ptr<Teste>(new Teste));
cout << "a->AnyNum: " << a->AnyNum << "\n";
return 0;
}
答案 1 :(得分:4)
是的,有:
void Z(Teste *value)
{
value->AnyNum = 100;
*_Z = *value; // you need assignment operator
}
编译器生成的赋值运算符不会生成深层副本,而是生成浅层副本。你要做的是为Teste
编写一个合适的赋值运算符(可能还有一个复制构造函数)。此外,在删除指针之前,您不必检查指针是否为NULL:
~Teste()
{
// no need for checking. Nothing will happen if you delete a NULL pointer
if (_Z != NULL)
DELETE(_Z);
}
答案 2 :(得分:2)
您还有另一个问题:_Z不是您应该使用的标识符。一般情况下,最好避免使用下划线,特别是保留大写字母后跟大写字母的双下划线或下划线。
答案 3 :(得分:2)
#ifndef DELETE
#define DELETE(var) delete var, var = NULL
#endif
我觉得很难看。 在使用课程时,它看起来非常不合适。你可以在变量超出范围的地方使用它,但它在析构函数中是浪费时间。我认为将代码包装在一些智能指针中更为简单:
class Teste
{
private:
Teste *_Z;
public:
Teste()
~Teste() // Delete the _Z pointer.
Teste *Z();
void Z(Teste *value);
};
确定。您有一个在析构函数中删除的指针成员。 这意味着您将获得指针的所有权。这意味着四分之一适用(类似于三条规则但适用于所有权规则)。这意味着你基本上需要编写4个方法,否则编译器生成的版本会搞乱你的代码。你应该写的方法是:
A Normal (or default constructor)
A Copy constructor
An Assignment operator
A destructor.
您的代码只有两个。你需要写另外两个。 或者您的对象不应该拥有RAW指针的所有权。即。使用智能指针。
Teste *_Z;
这是不允许的。 以下划线和国会大厦字母开头的标识符是保留的。 您冒着操作系统宏损坏代码的风险。停止使用下划线作为标识符的第一个字符。
~Teste(){
if (_Z != NULL)
DELETE(_Z);
}
这不是必需的。简单删除_Z本来没问题。 _Z超出范围,因为它在析构函数中,因此不需要将其设置为NULL。 删除操作符正好处理NULL指针。
~Test()
{ delete _Z;
}
Teste *Z(){
_Z = new Teste;
return _Z;
}
如果多次调用Z()会发生什么情况(PS将*放在Z旁边而不是旁边的Teste使其难以阅读)。 每次调用Z()时,成员变量_Z都会被赋予一个新值。但旧的价值会发生什么?基本上你是在泄漏它。还通过返回指向所拥有对象的指针 在Teste内部,你给别人机会滥用对象(删除它等)。这个不好。这种方法没有明确的所有权。
Teste& Z()
{
delete _Z; // Destroy the old value
_Z = new Teste; // Allocate a new value.
return *_Z; // Return a reference. This indicates you are retaining ownership.
// Thus any user is not allowed to delete it.
// Also you should note in the docs that it is only valid
// until the next not const call on the object
}
void Z(Teste *value){
value->AnyNum = 100;
*_Z = *value;
}
您正在将新构造的对象(包含指针)的内容复制到另一个动态创建的对象中! 如果没有先分配_Z会发生什么。构造函数将其设置为NULL,因此无法保证它具有有效值。 您分配的任何对象也应删除。但是这里的值被动态分配传递给Z但从未被释放。你逃避这个的原因是因为指针是c 当析构函数被销毁时,选择_Z和_Z被删除。
Teste *b = new Teste, *a;
真的听说过。不要懒得把它写出来。 这被认为是糟糕的风格,你永远不会通过任何代码审查。
Teste* b = new Teste;
Teste* a; // Why not set it to NULL
a = b->Z();
获取ab对象。但谁破坏了对象a或b?
b->Z(new Teste);
之后就太复杂了。
答案 4 :(得分:1)
(我试图将其添加为评论,但这会搞砸代码..)
我强烈建议不要使用
#ifndef DELETE
#define DELETE(var) delete var, var = NULL
#endif
但更像是
struct Deleter
{
template< class tType >
void operator() ( tType*& p )
{
delete p;
p = 0;
}
};
用法:
Deleter()( somePointerToDeleteAndSetToZero );
答案 5 :(得分:1)
(不是真正的答案,但评论不会这样做)
你定义你的宏的方式很容易出现一个微妙的错误(而且到目前为止没有人发现它的事实证明了这一点)。考虑一下你的代码:
if (_Z != NULL) // yes, this check is not needed, but that's not the point I'm trying to make
DELETE(_Z);
预处理器通过后会发生什么:
if (_Z != 0)
delete _Z; _Z = 0;
如果您仍然无法看到它,请让我正确缩进:
if (_Z != 0)
delete _Z;
_Z = 0;
这不是什么大问题,特别是条件,但它会爆炸其他任何东西,你会花年龄试图弄清楚为什么你的指针突然是NULL。这就是为什么内联函数比宏更受欢迎的原因 - 将它们弄得更加困难。
<小时/> 编辑:好的,你在宏定义中使用了逗号,这样你就安全了......但我仍然会说在这种情况下使用[inline]函数更安全。我不是那些不使用宏的人之一,但我不会使用它们,除非它们是绝对必要的并且它们不是这种情况
答案 6 :(得分:0)
void Z(Teste * value){ value-&gt; AnyNum = 100; * _Z = *值; }
和
b->Z(new Teste);
创建内存泄漏
'新Teste'永远不会被删除,而是你正在做的是分配一个新对象作为参数,然后使用* _Z = *值复制其中的任何内容,但是在调用后对象不会被删除。
如果你要写
Test* param - new Teste;
b->Z(param)
delete param;
会更好
当然大多数人会使用boost :: shared_ptr或类似的东西来避免关心删除等等