12.1
12.1 动态内存和类
静态类成员
初始化静态类成员:在类外面初始化静态类成员
int StringBad::num_string = 0;
不能在声明中初始化, 因为声明中描述的是如何分配内存, 但不分配内存。
静态类成员在类声明之外使用单独语句进行初始化, 因为静态类成员是单独存储的, 而不是对象的组成部分。
初始化没有使用static。
初始化不要在头文件中, 否则, 包含多个头文件可能出现多个初始化语句。
例外情况:静态数据成员为const整数类型或枚举类型。
StringBad类
自动存储对象被删除的顺序和创建的顺序相反 --- 栈。
复制构造函数
StringBad sailor = sports; // 相当于
StringBad sailor = StringBad(sports);
当使用一个对象来初始化另一个对象时, 编译器将自动生成上述构造函数(复制构造函数)
特殊成员函数
C++提供以下成员函数(如果没有定义):
默认构造函数
默认析构函数
复制构造函数
赋值运算符
地址运算符
后三个函数, 在使用的时候会生成定义。
C++11 提供了另外两个特殊成员函数:移动构造函数, 移动赋值函数。 第18章讨论。
默认构造函数
如果没有提供任何构造函数, C++将提供默认构造函数。
编译器将提供一个不接受任何参数, 不执行任何操作的构造函数。
带参数的构造函数也可以是默认构造函数, 只要它的所有参数都有默认值。
只能有一个默认构造函数。
复制构造函数
用于将一个对象复制到新创建的对象中。
它用于初始化中, 包括按值传递参数。
原型:
Class_name(const Class_name &);
何时调用复制构造函数:
新建一个对象并将其初始化为同类现有对象时, 都会调用复制构造函数。
StringBad ditto(motto);
StringBad ditto = motto;
StringBad ditto = StringBad(motto);
StringBad * pStringBad = new StringBad(motto);
其中, 中间两种可能使用复制构造函数直接创建对象, 也可能先生成一个临时对象, 再创建。
每当程序生成对象副本时, 都会使用复制构造函数。
默认的复制构造函数的功能:
默认构造函数逐个复制非静态成员(浅复制), 复制的是成员的值。
分析StringBad出现的问题
定义一个显示复制构造函数以解决问题:深度复制
如果类中包含使用new初始化的指针成员, 应当定义一个复制构造函数, 以复制指向的数据, 而不是指针, 称为深度复制。
StringBad的其他问题:赋值运算符
默认赋值运算符原型:
Class_name & Class_name::operator =(const Class_name &);
赋值运算符的功能以及何时使用:
将已有的对象赋给另一个对象时, 将使用重载的赋值运算符。
StringBad ditto = motto;
StringBad ditto = StringBad(motto);
如果初始化时使用了赋值运算符, 一定会调用复制构造函数, 也允许调用复制运算符。
提供赋值运算符:
进行深度复制。
使用delete []释放以前分配的数据。
避免将对象赋给自身, 否则, 在对象重新赋值前, 释放内存操作可能删除对象内容。
函数返回一个指向调用对象的引用。
StringBad & StringBad::operator =(const StringBad & s)
{
if (this == &s)
return *this; // 检查自我复制
delete [] str;
len = s.len;
str = new char [len + 1];
std::strcopy(str, s.str);
return *this;
}