12.1 动态内存和类
构造函数使用常规C字符串来初始化String对象
StingBad::StringBad(const char * s)
{
len = std::strlen(s);
str = new char[len + 1];
std::strcpy(str, s);
}
- 使用strlen()函数计算字符串的长度,并对len成员进行初始化。
- 使用new分配足够的空间来保存字符串,然后将新内存的地址赋给str成员【strlen()返回字符串的长度,但不包括末尾的空字符】
- 使用strcpy()将传递的字符串赋值到新的内容中【字符串并不保存在对象中,字符串单独保存在堆内存中,对象仅保存了指出到哪里去查询字符串的信息】
- 删除对象可以释放对象本省占用的内存,但并不能自动释放属于对象成员的指针指向的内存,因此,必须使用析构函数,在析构函数中使用delete语句可确保对象过期时,由构造函数使用new分配的内存被释放。
特殊的成员函数
- 默认构造函数,如果没有定义构造函数
- 默认析构函数,未定义
- 复制构造函数,未定义
- 赋值运算符,未定义
- 地址运算符,未定义
默认构造函数
- 带参数的构造函数也可以是默认的构造函数,只要所有的参数都有默认值
Klunk(int n = 0) {klunk_ct = n; }
复制构造函数
- 用于将一个对象复制到新创建的对象中
Class name(const Calss_name &); - 何时调用?新建一个对象并将其初始化为同类现有对象时,复制构造函数都将被调用,没当函数生成了对象副本时,编译器都会使用复制构造函数,具体地说,当函数按值传递对象或函数返回对象时,都将使用复制构造函数,按值传递意味着创建原始变量的一个副本,编译器生成临时对象时,也将使用复制构造函数
StringBad ditto(motto);
StringBad metoo = motto; - 由于按值传递对象将调用复制构造函数,因此应该按引用传递对象,这样节省调用构造函数的时间以及存储新对象的空间
默认的复制构造函数功能
-
默认的复制构造函数逐个复制非静态成员(成员赋值也被称为浅复制),复制的是成员的值
StringBad sailor = sports;
==
StringBad sailor;
sailor.str = sports.str;
sailor.len = sailor.len; -
如果成员本事就是类对象,则将使用这个类的复制构造函数来复制成员对象。静态成员不受影响,因为它们属于整个类,而不是各个对象
-
使用复制构造函数来创建一个临时对象,然后通过赋值将临时对象的值赋值到新对象中,这就是说,初始化总是会调用赋值构造函数,而使用=运算符时也允许调用赋值运算符
C++11引入新关键字 nullptr
12.2 改进后的String类
静态类成员函数
可以将成员函数声明为静态的,这样做有两个重要的后果
- 不能通过对象调用静态成员函数,如果静态成员函数是在公有部分声明的,则可以使用类名和作用域解析运算符来调用它
- 其次,由于静态成员函数不与特定的对象相关联,因此只能使用静态数据成员
12.3 在构造函数中使用new时应注意的事项
- 如果在构造函数中使用new来初始化指针成员,则应在析构函数中使用delete
- new和delete必须相互兼容,new对应delete,new[]对应delete[]
- 如果有多个构造函数,则必须以相同的方式使用new,要么都带中括号,要么都不带
- 应定义一个复制构造函数,通过深度复制将一个对象初始化为另一个对象
- 应定义一个复制运算符,通过深度复制将一个对象复制给另一个对象
String& String::operator=(const String& str)
{
if (this == &str)
return *this;
delete[] str;
len = st.len;
str = new char [len + 1];
std::strcpy(str, st.str);
return *this
}
12.4 有关返回对象的说明
使用const引用的常见原因是旨在提高效率
const Vector & Max(const Vector & v1, const Vector & v2)
{
if(v1.magval() > v2.magval())
return v1;
else
return v2;
}
- 首先,返沪对象将调用复制构造函数,而返回引用不会。因此,该版本工作更少,效率更高,其次,引用指向的对象应该在调用函数执行时存在。
12.7 队列模拟
成员初始化列表语法
Classy:Classy(int n, int m) :mem1(n), mem2(0), mem3(n*m + 2)
- 这些初始化工作时在对象创建时完成,此时还未执行括号中的任何代码
- 这种格式只能用于构造函数
- 必须用这个格式来初始化非静态const数据成员
- 必须用这个格式来初始化引用数据成员
数据成员被初始化的顺序与它们出现在类声明中的顺序相同,与初始化器中的排列顺序无关,不能将成员初始化列表语法用于构造函数之外的其他类方法
C++11允许以更直观的方式进行初始化
class Classy
{
int mem1 = 10;
const int mem2 = 20;
};