《C++ Primer Plus》第12章 类和动态内存分配

动态内存和类

动态内存分配让程序在运行时决定内存分配,而不是在编译时决定。

C++使用new和delete运算符来动态控制内存。

使用关键字static可以将类成员声明为静态存储类。对于静态类成员,无论创建了多少对象,程序都只创建一个静态类变量副本。这对于所有类对象都具有相同值的类私有数据是非常方便的。

不能在类声明中初始化静态成员变量,声明仅描述了如何分配内存,但并不分配内存。可在类声明之外使用单独的语句来进行初始化,因为静态类成员是单独存储的,而不是对象的组成部分。初始化语句需要使用作用域运算符并指出类型,但并不使用关键字static。

静态成员变量初始化在方法文件中进行,而不是在类声明文件中进行。因为类声明位于多个头文件中,程序可能将头文件包括在多个文件中,从而出现多个初始化语句副本,从而引发错误。

静态数据成员在类声明中声明,在包含类方法的文件中初始化。初始化时使用作用域运算符来指出静态成员所属的类。如果静态成员是const整数类型或枚举型,则可以在类声明中初始化。

当对象过期时,删除对象可以释放对象本身占用的内存,但并不能自动释放属于对象成员指针指向的内存。

在构造函数中使用new来分配内存时,必须在相应的析构函数中使用delete来释放内存。如果使用new[ ]来分配内存,则应使用delete[ ]来释放内存。

C++自动提供如下的成员函数:

  • 默认构造函数,如果没有定义构造函数
  • 默认析构函数,如果没有定义
  • 复制构造函数,如果没有定义
  • 赋值运算符,如果没有定义
  • 地址运算符,如果没有定义

如果没有提供任何构造函数,C++将创建默认构造函数,这个构造函数不接受任何参数,也不执行任何操作,在创建对象时会调用这个构造函数,且对象的值在初始化时未知。

如果定义了构造函数,C++将不会定义默认构造函数。如果希望在创建对象时不显示的对其进行初始化,则必须显示定义默认构造函数,这种构造函数可以没有任何参数,但可以使用它来设置特定的值,也可以带参数并且所有参数都有默认值。但只能有一个构造函数。

复制构造函数用于将一个对象复制到新创建的对象中,它用于初始化过程中(包括按值传递参数),而不是常规的赋值过程中。

类的复制构造函数原型为Class_name(const Class_name &),它接受一个指向类对象的常量引用作为参数。

创建一个对象并将其初始化为同类现有对象时,复制构造函数都将被调用:

  • 创建一个新对象并将其初始化为同类现有对象时
  • 当函数按值传递对象或函数返回对象时

由于按值传递对象将调用复制构造函数,因此应该按引用传递对象,这样可以节省调用复制构造函数的时间已经存储新对象的空间。

默认的复制构造函数逐个复制非静态成员(浅复制),复制的是成员的值(若成员值为指针,则复制该指针,而非指针所指向的数据)。如果成员本身就是类对象,则将使用这个类的复制构造函数来复制成员对象。

静态成员不受影响,因为它们属于整个类,而不是各个对象。

默认的复制构造函数不说明其行为,因此它不指出创建过程。但是析构函数在任何对象过期时都将被调用,无论对象是如何被创建的。这可能导致释放同一部分内存两次,从而造成程序异常终止。

如果类中包含随新对象创建而发生变化的数据成员时,需要提供一个显示复制构造函数来处理计数问题。

复制构造函数应当不仅仅复制指针成员的值,还要复制指针所指向地址的内容(深度复制),并将复制的指针副本赋给复制构造函数创建的对象,这样每个对象都有自己的字符串,而不是引用另一个对象的字符串。调用析构函数是都将释放不同的字符串,而不会去试图释放已经被释放的字符串。

必须定义复制构造函数的原因在于,一些类成员是使用new初始化的、指向数据的指针,而不是数据本身。

如果类中包含了使用new初始化的指针成员,应当定义一个复制构造函数,以复制指向的数据,而不是指针,这被称为深度复制。复制的另一种形式(成员复制或浅复制)只是复制指针值。

将已有的对象赋给另一个对象时,将使用重载的复制运算符。

赋值运算符:

  • 由于目标对象可能引用了以前分配的数据,所以函数应使用delete[ ]来释放这些数据
  • 函数应当避免将对象赋给自身,否则,给对象重新赋值前,释放内存操作可能删除对象内容
  • 函数返回一个指向调用对象的引用

12.2

 

你可能感兴趣的:(《C++ Primer Plus》第12章 类和动态内存分配)