在运行时创建数组优于在编译时创建数组,对于结构也是如此。需要在程序运行时为结构分配所需的空间,这也可以使用new运算符来完成。通过使用new,可以创建动态结构。同样,“动态”意味着内存是在运行时,而不是编译时分配的。由于类与结构非常相似,因此本节介绍的有关结构的技术也适用于类。
将new用于结构由两步组成:创建结构和访问其成员。要创建结构,需要同时使用结构
类型和new。
比较棘手的一步是访问成员。创建动态结构时,不能将成员运算符句点用于结构名,因为这种结构没有名称,只是知道它的地址。C++专门为这种情况提供了一个运算符:箭头成员运算符(−>)。该运算符由连字符和大于号组成,可用于指向结构的指针,就像点运算符可用于结构名一样。例如,如果ps指向一个inflatable结构,则ps−>price是被指向的结构的price成员(参见图4.11)。
提示:
有时,C++新手在指定结构成员时,搞不清楚何时应使用句点运算符,何时应使用箭头运算符。规则非常简单。如果结构标识符是结构名,则使用句点运算符;如果标识符是指向结构的指针,则使用箭头运算符。
提示:
有时,C++新手在指定结构成员时,搞不清楚何时应使用句点运算符,何时应使用箭头运算符。规则非常简单。如果结构标识符是结构名,则使用句点运算符;如果标识符是指向结构的指针,则使用箭头运算符
另一种访问结构成员的方法是,如果ps是指向结构的指针,则*ps就是被指向的值—结构本身。由于*ps是一个结构,因此(*ps).price是该结构的price成员。C++的运算符优先规则要求使用括号。
程序清单4.21使用new创建一个未命
的结构,并演示了两种访问结构成员的指针表示法。
using namespace std;
struct inflatable
{
char name[20];
float volume;
double price;
};
int main()
{
inflatable * ps = new inflatable;
cout << "Enter name of inflatable item ";
cin.get(ps->name,20);
cout << "Enter valume ";
cin >> (* ps).volume;
cout << "Enter price :";
cin >> ps->price;
cout << "Name " << (*ps).name << endl;
cout << "Volume : " << ps->volume << endl;
cout << "Price " << ps->price << endl;
delete ps;
system("pause");
return 0;
}
结果如下:
程序清单4.22还是演示了一些技巧。另外,为演示delete是如何工作的,该程序还用它来释放内存以便能够重新使用。
来看一下程序清单4.22中的函数getname( )。它使用cin将输入的单词放到temp数组中,然后使用new分配新内存,以存储该单词。程序需要strle(temp)+ 1个字符(包括空字符)来存储该字符串,因此将这个值提供给new。获得空间后,getname( )使用标准库函数strcpy( )将temp中的字符串复制到新的内存块中。该函数并不检查内存块是否能够容纳字符串,但getname( )通过使用new请求合适的字节数来完
来看一下程序清单4.22中的函数getname( )。它使用cin将输入的单词放到temp数组中,然后使用new分配新内存,以存储该单词。程序需要strle(temp)+ 1个字符(包括空字符)来存储该字符串,因此将这个值提供给new。获得空间后,getname( )使用标准库函数strcpy( )将temp中的字符串复制到新的内存块中。该函数并不检查内存块是否能够容纳字符串,但getname( )通过使用new请求合适的字节数来完
成了这样的工作。最后,函数返回pn,这是字符串副本的地址。
在main( )中,返回值(地址)被赋给指针name。该指针是在main( )中定义的,但它指向getname( )函数中分配的内存块。然后,程序打印该字符串及其地址。
接下来,在释放name指向的内存块后,main( )再次调用getname( )。C++不保证新释放的内存就是下一次使用new时选择的内存,从程序运行结果可知,确实不是。
在这个例子中,getname( )分配内存,而main( )释放内存。将new和delete放在不同的函数中通常并不是个好办法,因为这样很容易忘记使用delete。不过这个例子确实把new和
delete分开放置了,只是为了说明这样做也是可以的。
4.8.5 自动存储、静态存储和动态存储
自动存储
在函数内部定义的常规变量使用自动存储空间,被称为自动变量(automatic variable),这意味着它们在所属的函数被调用时自动产生,在该函数结束时消亡。
实际上,自动变量是一个局部变量,其作用域为包含它的代码块。代码块是被包含在花括号中的一段代码。 自动变量通常存储在栈中。这意味着执行代码块时,其中的变量将依次加入到栈中,而在离开代码块时,将按相反的顺序释放这些变量,这被称为后进先出(LIFO)。因此,在程序执行过程中,栈将不断地增大和缩小。
静态存储
静态存储是整个程序执行期间都存在的存储方式。使变量成为静态的方式有两种:一种是在函数外面定义它;另一种是在声明变量时使用关键字static:
自动存储和静态存储的关键在于:这些方法严格地限制了变量的寿命。变量可能存在于程序的整个生命周期(静态变量),也可能只是在特定函数被执行时存在(自动变量)。
动态存储
new和delete运算符提供了一种比自动变量和静态变量更灵活的方法。它们管理了一个内存池,这在C++中被称为自由存储空间(free store)或堆(heap)。该内存池同用于静态变
量和自动变量的内存是分开的。
如果使用new运算符在自由存储空间(或堆)上创建变量后,没有调用delete,将发生什么情况呢?如果没有调用delete,则即使包含指针的内存由于作用域规则和对象生命周期的原因而被释放,在自由存储空间上动态分配的变量或结构也将继续存在。实际上,将会无法访问自由存储空间中的结构,因为指向这些内存的指针无效。这将导致内存泄漏。
被泄漏的内存将在程序的整个生命周期内都不可使用;这些内存被分配出去,但无法收回。极端情况(不过不常见)是,内存泄漏可能会非常严重,以致于应用程序可用的内存被耗尽,出现内存耗尽错误,导致程序崩溃。另外,这种泄漏还会给一些操作系统或在相同的内存空间中运行的应用程序带来负面影响,导致它们崩溃。
即使是最好的程序员和软件公司,也可能导致内存泄漏。要避免内存泄漏,最好是养成这样一种习惯,即同时使用new和delete运算符,在自由存储空间上动态分配内存,随后便释放它。