不是告诉您何时使用
delete
,而是尝试解释为什么要使用指针。这样您就可以决定何时使用动态对象,如何使用它们以及何时调用
delete
(并且不要)。
经验法则:
- 尽可能使用静态对象,然后在需要时创建对该实例的指针。不需要调用
delete
。
- 如果您创建了指向动态对象的指针,请创建清理代码。因此,当您编写
new
时,也请在适当位置编写delete
(并确保已调用)。
- 每个
new
关键字都需要有一个delete
关键字。否则,您将占用机器的所有资源,导致应用程序崩溃或停止。这还会使系统变慢。
静态创建对象:
Fraction f1;
- 不需要删除任何东西,这是在离开其创建的搜罗时处理的。
对象的动态创建:
Fraction* f1;
现在你拥有了一个指向堆内存块的地址。但由于你尚未为它分配任何值,因此它是无效的。根据情况,最好将其赋值为 NULL
(Windows) 或 0
(跨平台)。
Fraction* f1 = 0
何时使用delete
一旦创建动态对象,即调用new
运算符,就需要在某个地方调用delete
。
int main()
{
Fraction* f1 = 0;
f1 = new Fraction();
if( f1 )
{
delete f1;
f1 = 0;
}
return 0;
}
在某些情况下,拥有一个
Fraction
数组或指向它的指针可能会很有用。这里为了简单起见使用
int
,跳过错误处理:
int arr[ 10 ];
int cur = -1;
int* Add( int fraction )
{
arr[++cur] = fraction;
return &arr[cur];
}
Add( 1 );
Add( 4 );
这里发生的一件事情是,没有通过动态对象分配任何内存的赋值。它们会自动释放。函数返回的指针是指向静态内存块的指针。
当将arr
定义为指向int
的指针时:
int* arr[ 10 ];
int cur = -1;
int* Add( int* fraction )
{
arr[++cur] = fraction;
return arr[cur];
}
int* test;
test = Add( new int( 1 ) );
test = Add( new int( 4 ) );
现在你有两个内存块泄露了,因为你没有清理代码。
当你在每个Add(...)
之后调用delete test
时,你清理了内存,但是你失去了存储在int* arr[ 10 ]
中的值,因为它们指向持有该值的内存。
你可以创建另一个函数,在使用这些值后调用它:
void CleanUp()
{
for( int a = 0; a < 10; ++a )
delete arr[ a ];
}
一个小的使用示例:
int* test;
int test2;
test = Add( new int( 1 ) );
test2 = *Add( new int( 4 ) ); // dereference the returned value
CleanUp();
为什么我们要使用指针:
int Add( int val )
{
return val;
}
当您调用需要参数(类型)的函数时,您传递的不是实例本身,而是它的副本。在上面的函数中,您返回了该副本的一个副本。这将导致大量的内存复制和重复,使您的应用程序变得非常缓慢。
考虑以下内容:
class Test
{
int t;
char str[ 256 ];
}
如果一个函数需要类型为
Test
的数据,你可能会复制
int
和256个字符。因此,将函数设计成只需要指向
Test
的指针即可。这样,指针所指向的内存就会被使用,而不需要进行复制。
int Add( int val )
{
val++;
return val;
}
在这个例子中,我们将 1 添加到 val
的副本中,然后返回该副本的副本。
int i = Add( 1 );
结果:i = 2;
void Add( int* val )
{
*val++;
}
在这个例子中,你将地址传递给一个值,然后在取消引用后将一个值加一。
int i = 1;
Add( &i );
结果: i = 2;
现在你已经传入i的地址,而不是复制它。在函数内部,你直接将1添加到该内存块中的值。由于你改变了内存本身,所以没有返回任何内容。
空指针/测试有效指针
有时你会遇到如下例子:
if( p != 0 ) // or if( p )
{
/* do something with p */
}
这只是为了检查指针p
是否有效。但是,无效的地址 - 未指向您已保留的内存(访问冲突) - 也将通过。对于您的代码,无效指针是一个有效地址。
因此,要使用这样的检查,您必须将指针NULL
(或0
)。
Fraction* f1 = 0
当f1 == 0
时,它不指向任何东西,否则它会指向它指向的任何东西。
当您在“main”类中有一个指针而该指针已创建或尚未创建时,这将非常有用。
class Fraction
{
public:
int* basicFeature;
int* ExtendedFeature = 0;
Fraction( int fraction )
{
basicFeature = new int( fraction );
}
Fraction( int fraction, int extended )
: Fraction( fraction )
{
ExtendedFeature = new int( extended );
}
~Fraction()
{
delete basicFeature;
if( ExtendedFeature )
delete ExtendedFeature;
}
}
在构造函数中,我们创建了两个指针,因此在析构函数中,我们清理这些指针。仅检查ExtendedFeature
,因为它可能已经创建或未创建。 basicFeature
始终被创建。
您可以通过调用一个新函数removeExtendedFeature()
来替换包括其范围在内的析构函数中的if
语句,该函数的实现如下:
Fraction::removeExtendedFeature()
{
if( ExtendedFeature )
{
delete ExtendedFeature;
ExtendedFeature = 0;
}
}
还有新的析构函数:
Fraction::~Fraction()
{
delete basicFeature;
removeExtendedFeature();
}
另一个空值的功能可能是:
int Fraction::getValue()
{
int result = *basicFeature;
if( ExtendedFeature )
result += *ExtendedFeature;
return result;
}
非常抱歉,我的Fraction类非常糟糕,而且扩展功能也更加糟糕。但是作为一个例子,它可以起到作用。
new
。将其添加到需要考虑的事项列表中。 - WhozCraigif (!f1) delete f1;
不会删除对象,因为它只会在指针为null
或0
时才删除。无论如何,在删除之前不应该测试指针,因为如果你删除一个空指针它什么也不会做。所以直接执行delete f1;
即可。 - Galik