尽管现代机器的内存容量越来越大,但是自由存储区总有可能被耗尽。如果程序用完了所有可用的内存,new
表达式就有可能失败。如果 new
表达式无法获取需要的内存空间,系统将抛出名为 bad_alloc
的异常。我们将在第 6.13 节介绍如何抛出异常。
动态创建的对象用完后,程序员必须显式地将该对象占用的内存返回给自由存储区。C++ 提供了 delete
表达式释放指针所指向的地址空间。
delete pi;
frees the memory associated with the int
object addressed by pi
.
该命令释放 pi
指向的 int
型对象所占用的内存空间。
|
如果指针指向不是用 |
C++ 没有明确定义如何释放指向不是用 new
分配的内存地址的指针。下面提供了一些安全的和不安全的 delete expressions 表达式。
int i;
int *pi = &i;
string str = "dwarves";
double *pd = new double(33);
delete str; // error: str is not a dynamic object
delete pi; // error: pi refers to a local
delete pd; // ok
值得注意的是:编译器可能会拒绝编译 str
的 delete
语句。编译器知道 str
并不是一个指针,因此会在编译时就能检查出这个错误。第二个错误则比较隐蔽:通常来说,编译器不能断定一个指针指向什么类型的对象,因此尽管这个语句是错误的,但在大部分编译器上仍能通过。
如果指针的值为 0
,则在其上做 delete
操作是合法的,但这样做没有任何意义:
int *ip = 0; delete ip; // ok: always ok to delete a pointer that is equal to 0
C++ 保证:删除 0 值的指针是安全的。
delete
之后,重设指针的值
执行语句
delete p;
后,p
变成没有定义。在很多机器上,尽管 p
没有定义,但仍然存放了它之前所指向对象的地址,然而 p
所指向的内存已经被释放,因此 p
不再有效。
删除指针后,该指针变成悬垂指针。悬垂指针指向曾经存放对象的内存,但该对象已经不再存在了。悬垂指针往往导致程序错误,而且很难检测出来。
|
一旦删除了指针所指向的对象,立即将指针置为 0,这样就非常清楚地表明指针不再指向任何对象。 |
const
对象的动态分配和回收
C++ 允许动态创建 const
对象:
// allocate and initialize a const object const int *pci = new const int(1024);
与其他常量一样,动态创建的 const
对象必须在创建时初始化,并且一经初始化,其值就不能再修改。上述 new 表达式返回指向 int
型 const
对象的指针。与其他 const
对象的地址一样,由于 new
返回的地址上存放的是const
对象,因此该地址只能赋给指向 const
的指针。
对于类类型的 const
动态对象,如果该类提供了默认的构造函数,则此对象可隐式初始化:
// allocate default initialized const empty string const string *pcs = new const string;
new
表达式没有显式初始化 pcs
所指向的对象,而是隐式地将 pcs
所指向的对象初始化为空的 string
对象。内置类型对象或未提供默认构造函数的类类型对象必须显式初始化。
警告:动态内存的管理容易出错
下面三种常见的程序错误都与动态内存分配相关:
操纵动态分配的内存时,很容易发生上述错误,但这些错误却难以跟踪和修正。 |
const
对象
尽管程序员不能改变 const
对象的值,但可撤销对象本身。如同其他动态对象一样, const
动态对象也是使用删除指针来释放的:
delete pci; // ok: deletes a const object
即使 delete
表达式的操作数是指向 int
型 const
对象的指针,该语句同样有效地回收 pci
所指向的内容。
#########################################################################################################
2.c++实现原理篇
下面的发帖总结一下是:
1.new[]会在申请到的内存前面加入一些信息表示这申请到的内存被划分为了多少单元(即new出的数组有多长),这个信息只用于确定调用多少次析构函数时用到,在确定释放多大内存空间时用不到,因为系统自然知道要释放的内存大小是多少,系统在内存分配表中有记录。
2.delete和delete[]都是会正确的把全部的内存释放掉的,但是delete只会调用数组第一个元素的析构函数,delete[]会调用全部数组单元的析构函数,这就用到了上一条中new[]存储的数组长度,delete[]利用这个长度确定在哪些地方调用多少次析构函数。
3.delete和delete[]只在调用析构函数上有差别,在释放内存上是一样的,而且两者释放内存时为了确定要释放多长的内存它们都是通过查系统的内存分配表来实现的,1中说道的长度仅仅在delete[]确定调用多少析构函数时用到。因为管理内存是操作系统的事(因此操作系统知道这内存分配了多少),但调用析构函数则是编译器的事(因此编译器需要知道要调用多少次,这就是1中提到的new[]做的事)。
4.通过上面三条,我们知道delete和delete[]只有在面对自定义对象数组时才有差别,面对系统内置对象时是无差别的,即:
int* p = new int[10],那么delete p和delete[] p是没任何差别的。
你问的很好,许多人不知道对于内置类型来说,delete和delete []是一样的,看起来你是知道的
原因如下,事实上new和delete都是两步操作,分配(释放)堆内存和调用构造(析构)函数
对于堆内存的释放,确实不需要做额外的事情,delete和delete []无差别
区别就在于调用析构函数这一步,如果用delete只会调用第一个对象的析构,只有调用delete[]才会调用全部的析构
你问的很好,许多人不知道对于内置类型来说,delete和delete []是一样的,看起来你是知道的
原因如下,事实上new和delete都是两步操作,分配(释放)堆内存和调用构造(析构)函数
对于堆内存的释放,确实不需要做额外的事情,delete和delete []无差别
区别就在于调用析构函数这一步,如果用delete只会调用第一个对象的析构,只有调用delete[]才会调……
讲的太好了,我测试了一下;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include <iostream>
using
namespace
std;
class
A
{
public
:
A(){cout<<
"A\n"
;}
~A(){cout<<
"~A\n"
;}
};
int
main()
{
A *p=
new
A[10];
delete
p;
}
|
g++编译输出是:
1
2
3
4
5
6
7
8
9
10
11
|
A
A
A
A
A
A
A
A
A
A
~A
|
语言是发展中的,早期C++的delete []中还让你填你申请的单位个数呢,确实如你所说现在代码中如果全写delete[]就可以了,那是不是以后会向这方面发展,就说不清了
语言是发展中的,早期C++的delete []中还让你填你申请的单位个数呢,确实如你所说现在代码中如果全写delete[]就可以了,那是不是以后会向这方面发展,就说不清了难道是历史残留问题导致的??
语言越智能效率越低下...
不是不可以
c++设计者完全可以按你的要求设计出...
就像现在很多语言一样, 相比C++ 智能多了,...
但是代价就是效率的降低...
delete[]就相当于
for(;;)delete
但是还是没有解决我的问题啊;
因为调用析构函数的时候,已经知道有多少个对象了,
为什么不全部调用呢;还分为一个和多个这两种情况
如果是二维数组,你该如何delete呢?请问(如果不循环的话?)
我认为主要是为了分配和释放要配对, 运算符new/delete是可以重载的。这意味着可以任意多样的资源(包括内存,但不限于内存)管理策略,比如placement delete,只有对象的析构,没有释放内存到堆, 再比如placement new, 没有从堆申请存储。如果统一, 显然就失去了更多的灵活性。
语言是发展中的,早期C++的delete []中还让你填你申请的单位个数呢,确实如你所说现在代码中如果全写delete[]就可以了,那是不是以后会向这方面发展,就说不清了
引用 11 楼 的回复:
语言是发展中的,早期C++的delete []中还让你填你申请的单位个数呢,确实如你所说现在代码中如果全写delete[]就可以了,那是不是以后会向这方面发展,就说不清了
在c++2.0之前,将数组的大小提供给delete,是程序员的责任,比如
A* pA = new A[5];
delete[5] pA;
到了c++2.1,可以使用
……
引用 45 楼 的回复:我认为主要是为了分配和释放要配对, 运算符new/delete是可以重载的。这意味着可以任意多样的资源(包括内存,但不限于内存)管理策略,比如placement delete,只有对象的析构,没有释放内存到堆, 再比如placement new, 没有从堆申请存储。如果统一, 显然就失去了更多的灵活性。
并不存在placement delete
如果要配对的……