在C和C++中,有三种基本的存储使用区:
在静态存储区中,连接器(
linker)根据程序的需求为对象分配空间。全局变量、静态类成员以及函数中的静态变量都被分配在该区域中。一个在该区域中分配的对象只被构造一次,其生存期一直维持到程序结束。在程序运行的时候其中的地址是固定不变的。在使用线程(thread,共享地址空间的并发物)的程序里,静态对象可能会引起一些问题,因为这时的静态对象是被共享的,要对其正常访问就需要进行锁定操作。
函数的参数和局部变量被分配在此。对同一个函数或区块的每一处调用,其在该区域内都有自己单独的位置。这种存储被自动创建和销毁;因而才叫做“自动存储区”。自动存储区也被称为是“在
栈上的(
be on the stack)”。
在该区域中,
程序必须明确的为对象申请空间,并可以在使用完毕之后释放申请到的空间(使用
new和delete运算符)。当程序需要其中更多的空间时,就使用new向操作系统提出申请。通常情况下,自由存储区(也被称作动态存储区或者堆(heap))在一个程序的生存期内是不断增大的,因为其间被其它程序占用的空间从来都不被归还给操作系统。例如:
在C/C++中,允许在用户程序执行时动态的为对象分配存储。在C中,使用malloc()或calloc()、realloc()、free()来动态的分配、释放存储空间;在C++中,使用new()、delete()来动态的分配、释放存储空间。C++中的new()与C中的malloc()相比,有以下两个优点:
<1>new()自动返回正确的指针类型,不需要对返回指针进行类型转换。
int
*
p
=
new
int
[
20
];
<2> new()可以将创建的对象初始化。
int
*
p
=
new
int(20);
释放内存时,无论是由malloc()还是由new一次性分配的内存空间,必须一次性释放。该内存块可以是n个连续的自定义struct空间或n个连续基本类型空间。
<3>函数中分配内存
如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”,如下所示
void GetMemory2(char **p, int num)
{
*p =
new char[sizeof(char) * num];
}
|
void Test2(void)
{
char *str = NULL;
GetMemory2(&str, 100); // 注意参数是 &str,而不是str
strcpy(str, "hello");
cout<< str << endl;
free(str);
}
|
使用
new创建对象数组时,不能对各个对象进行初始化。对对象数组中的每一个对象进行初始化有以下两个方法:
<1>定义专门函数来完成初始化(如下表右);
<2>在类中增加不带参数的构造函数来初始化(如下表左)。
class
point
{
public
:
int
x
,
y
;
point
(
int
ox
,
int
oy
);
point
();
};
point::point
(
int
ox
,
int
oy
){
x
=
ox
;
y
=
oy
;
}
point::point
(){
x
=
10
;
y
=
10
;
}
void
main
()
{
point
*
p1
=
new
point
(
3
,
3
);
//声明对象指针,创建对象并调用带参数的构造函数,初始化对象
point
*
p2
=
new
point
[
5
];
//声明对象数组指针,创建对象并调用不带参数的构造函数,初始化对象
delete
p1;
delete
[]
p2
;
}
|
Class
point
{
Public
:
int
x
,
y
;
void
setPoint
(
int
ox,
int
oy);
};
void
point
::
setPoint
(
int
ox,
int
oy){
x = ox;
y = oy;
}
void
main
()
{
point
*
p2
=
new
point
[
5
];
//声明对象数组指针,创建对象
for
(
int
i
=
0
;
i
<
5
;
i
++)
p2[
i
].setPoint(
10
,
10
);
//调用
setPoint函数对成员变量促使化
delete
[]
p2
;
}
|
类的每个指针成员要么指向有效的内存,要么就指向空,在析构函数里可简单地
delete,而不用担心该指针是不是被new过。
使用
new()创建多维数组时,必须提供各维的大小。如:
int
(*
l
)[
2
];
l
=
new
int
[
2
][2]; //ok
l
=
new
int
[
2
][]; //error
l
=
new
int
[][
2
]; //error
创建两维数组,举例
int
**
p
=
new
int
*[
3
];
//首先分配指针数组中各个指针(p[0],p[1],p[2])的空间(int*型空间,非int型空间)
for
(
int
i
=
0
;
i
<
3
;
i
++)
{
p
[
I
] =
new
int
[
3
];
p
[
I
][
0
] =
0
;
p
[
I
][
1
] =
1
;
p
[
I
][
2
] =
2
;
}
for
(
i
=
0
;
i
<
3
;
i
++)
{
delete
[]
p
[
i
];
p
[
I
] =
NULL
;
}
delete
[]
p
;
p
=
NULL
;
|
int
(*
l
)[
2
];
//指向一维数组的指针
l
=
new
int
[
2
][
2
];
for
(
i
=
0
;
i
<
2
;
i
++)
for
(
int
j
=
0
;
j
<
2
;
j
++)
l
[
i
][
j
] =
i
+
j
;
delete
[]
l
;
|
用free或delete释放了内存时,如果后面要用到指针后,应立即将指针设置为NULL,防止产生“野指针”。
重载new要和重载delete匹配。
重载一个与类相关的new和delete函数,只需重载运算符函数成为该类的成员函数。此时重载的new和delete仅用于该特定的类,在其他数据类型上仍然使用原始版本的new和delete。即遇到new和delete时,编译程序首先检查正在使用对象所在的类是否重载了new和delete,如果重载了,则使用这个重载版本;否则,使用全局定义的new和delete。示例如下表左部:
class
A
{
int
x
,
y
,
z
;
public
:
A
(
int
x1
,
int
y1
);
~
A
(){
}
void
*
operator
new(
unsigned
int
size
);
void
operator
delete(
void
*
p
);
};
A
::
A
(
int
x1
,
int
y1
)
{
x
=
x1
;
y
=
y1
;
}
void
*
A
::
operator
new (
unsigned
int
size
)
{
return
malloc
(
size
);
//return (void *)new char[sizeof(A)];
}
void
A
::
operator
delete (
void
*
p
)
{
free
(
p
);
//delete p;
}
void
main
(
void
)
{
A
*
a
;
a
=
new
A
(
3
,
10
); //调用类A的重载运算符new
delete
a
; //调用类A的重载运算符delete
int
*
i
=
new
int
(
100
); //调用系统运算符new
delete
i
; //调用系统运算符delete
}
|
class
A
{
int
x
,
y
,
z
;
public
:
A
(
int
x1
,
int
y1
);
~
A
(){
}
};
A
::
A
(
int
x1
,
int
y1
)
{
x
=
x1
;
y
=
y1
;
}
void
*
operator
new (
unsigned
int
size
)
{
return
malloc
(
size
);
//ret
urn (
void
*)
new
char
[size];
}
void
operator
delete (
void
*
p
)
{
free
(
p
);
//delete p;
}
void
main
(
void
)
{
A
*
a
;
a
=
new
A
(
3
,
10
); //调用重载运算符new
delete
a
; //调用重载运算符delete
int
*
i
=
new
int
(
100
); //调用重载运算符new
delete
i
; //调用重载运算符delete
}
|
在任何类外重载new和delete,使它成为全局的。此时,C++中原来的new和delete被忽略,程序中使用重载的new和delete。示例如上表右部: