感觉这部分比较好,而且非常有用,所以就转过来~
在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 = newint[20];
<2> new()可以将创建的对象初始化。
int *p = newint(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>在类中增加不带参数的构造函数来初始化(如下表左)。
classpoint {
public:
intx, y;
point(intox, intoy);
point();
};
point::point(intox, intoy){
x = ox;
y = oy;
}
point::point(){
x =10;
y =10;
}
voidmain()
{
point *p1 = newpoint(3,3); //声明对象指针,创建对象并调用带参数的构造函数,初始化对象
point *p2 = newpoint[5]; //声明对象数组指针,创建对象并调用不带参数的构造函数,初始化对象
delete p1;
delete []p2;
}
|
Classpoint {
Public:
intx, y;
voidsetPoint(int ox, int oy);
};
voidpoint::setPoint(int ox, int oy){
x = ox;
y = oy;
}
voidmain()
{
point *p2 = newpoint[5]; //声明对象数组指针,创建对象
for(inti=0; i<5; i++)
p2[i].setPoint(10, 10);//调用setPoint函数对成员变量促使化
delete []p2;
}
|
类的每个指针成员要么指向有效的内存,要么就指向空,在析构函数里可简单地delete,而不用担心该指针是不是被new过。
使用new()创建多维数组时,必须提供各维的大小。如:
int (*l)[2];
l= newint[2][2]; //ok
l= newint[2][]; //error
l= newint[][2]; //error
创建两维数组,举例
int **p = newint*[3];//首先分配指针数组中各个指针(p[0],p[1],p[2])的空间(int*型空间,非int型空间)
for(inti = 0; i < 3; i++)
{
p[I] = newint [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= newint[2][2];
for(i = 0; i <2;i++)
for(intj = 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。示例如下表左部:
classA
{
intx,y,z;
public:
A(intx1, inty1);
~A(){
}
void * operator new(unsignedintsize);
voidoperator delete(void *p);
};
A::A(intx1, inty1)
{
x=x1; y=y1;
}
void * A::operator new (unsignedintsize)
{
returnmalloc(size);
//return (void *)new char[sizeof(A)];
}
voidA::operator delete (void *p)
{
free(p);
//delete p;
}
voidmain(void)
{
A *a;
a = newA(3,10); //调用类A的重载运算符new
deletea; //调用类A的重载运算符delete
int *i = newint(100); //调用系统运算符new
deletei; //调用系统运算符delete
}
|
classA
{
intx,y,z;
public:
A(intx1, inty1);
~A(){
}
};
A::A(intx1, inty1)
{
x=x1; y=y1;
}
void * operator new (unsignedintsize)
{
returnmalloc(size);
//return (void *)newchar[size];
}
voidoperator delete (void *p)
{
free(p);
//delete p;
}
voidmain(void)
{
A *a;
a = newA(3,10); //调用重载运算符new
deletea; //调用重载运算符delete
int *i = newint(100); //调用重载运算符new
deletei; //调用重载运算符delete
}
|
在任何类外重载new和delete,使它成为全局的。此时,C++中原来的new和delete被忽略,程序中使用重载的new和delete。示例如上表右部: