条款5:了解C++默认编写并调用哪些函数
1、空类
空类本来是没有存储空间,但为了能在内存中找到该类,编译器为该类添加一个char类型的成员,用来唯一标识该类在内存的位置
编译器为空类自定义哪些函数,这些函数都是public并且inline
①默认构造函数
②默认析构函数
③默认拷贝构造函数
④默认拷贝赋值函数
2、默认构造函数
当我们自定义构造函数,编译器就不会为我们定义一个缺省构造函数
3、默认析构函数
只有当基类定义virtual析构函数,继承的derive class才默认具有虚属性,条款7会详细介绍。
4、默认拷贝构造函数
C++中对象的复制就如同“克隆”,用一个已有的对象快速地复制出多个完全相同的对象。一般而言,以下三种情况都会使用到对象的复制:
①建立一个新对象,并用另一个同类的已有对象对新对象进行初始化
②当函数的参数为类的对象时,这时调用此函数时使用的是值传递,也会产生对象的复制;
③函数的返回值是类的对象时,在函数调用结束时,需要将函数中的对象复制一个临时对象并传给改函数的调用处。
①建立一个新对象,并用另一个同类的已有对象对新对象进行初始化
②当函数的参数为类的对象时,这时调用此函数时使用的是值传递,也会产生对象的复制;
③函数的返回值是类的对象时,在函数调用结束时,需要将函数中的对象复制一个临时对象并传给改函数的调用处。
默认拷贝构造函数仅仅使用“老对象”的数据成员的值对“新对象”的数据成员一一进行赋值
class Book
{
private:
std::string m_name;
int m_id;
};
Book s1;
Book s2 = s1; //调用编译器的拷贝构造函数,复制一本书
//编译器为我们生成的拷贝构造函数
Book(const Book &r)
{
this->m_name = r.m_name; //调用string类的拷贝构造函数完成
this->m_id = r.m_id;
}
类中有静态成员
如果让一个静态成员统计该种书的数量会是什么情况呢?
class Book
{
public:
<span style="white-space:pre"> </span>Book() {++m_iCount;}
<span style="white-space:pre"> </span>~Book() {--m_iCount;}
private:
<span style="white-space:pre"> </span>std::string m_name;
<span style="white-space:pre"> </span>int m_id;
<span style="white-space:pre"> </span>static int m_iCount;
};
那么不管我们复制多少本书,最终计数还是1,而且析构书时,计数变成负数
类中有动态数据成员
所谓浅拷贝,指的是在对象复制时,只是对对象中的数据成员进行简单的赋值,上面的例子都是属于浅拷贝的情况,默认拷贝构造函数执行的也是浅拷贝。但是一旦对象存在了动态成员,那么浅拷贝就会出问题。
class Book
{
public:
Book(const char *name, const int id) :m_id(id)
{
m_name = new char[strlen(name) + 1];
strcpy(this->m_name, name);
}
~Book()
{
if (m_name != NULL)
delete[] m_name;
m_name = NULL; //可防止同一个对象内存被释放两次
}
private:
char *m_name;
const int m_id;
};
Book s1("match", 1);
Book s2 = s1;
s2调用默认拷贝构造函数,s2.m_name与s1.m_name指向同一个内存区域,即任意一个对值m_name的操作都会影响到另一个,而且还可能导致同一块内存被释放两次.这种情况,需要“深拷贝”来解决。
深拷贝
深拷贝就不是简单的赋值,而且重新分配一块空间,这就需要我们自定义拷贝构造函数和拷贝赋值函数。
在Book类中定义拷贝构造函数:
Book(const Book & book):m_id(book.m_id)
{
this->m_name = new char[strlen(book.m_name) + 1];
strcpy(this->m_name, book.m_name);
}
5、默认拷贝赋值函数
class Book
{
public:
Book(std::string &name, const int id) :m_name(name), m_id(id)
{
}
~Book() {}
private:
std::string &m_name;
const int m_id;
};
调用
std::string str1 = "match";
std::string str2 = "telno";
Book s1(str1, 1);
Book s2(str2, 2);
s2 = s1;
s2调用默认拷贝赋值函数,但赋值函数是对每一个成员赋值,类中有const和引用成员,在C++中是不允许重新赋值的,编译器会报错,因此在类中有引用成员和常量成员,需要自定义赋值函数,或者禁用它,下个条款说明。