[TOC]
概述
我们先说一下对象的创建过程。严格来说,对象的创建包括两个阶段,首先要分配内存空间,然后再进行初始化:
- 分配内存很好理解,就是在堆区、栈区或者全局数据区留出足够多的字节。这个时候的内存还比较“原始”,没有被“教化”,它所包含的数据一般是零值或者随机值,没有实际的意义。
- 初始化就是首次对内存赋值,让它的数据有意义。注意是首次赋值,再次赋值不叫初始化。初始化的时候还可以为对象分配其他的资源(打开文件、连接网络、动态分配内存等),或者提前进行一些计算(根据价格和数量计算出总价、根据长度和宽度计算出矩形的面积等)等。说白了,初始化就是调用构造函数。
很明显,这里所说的拷贝是在初始化阶段进行的,也就是用其它对象的数据来初始化新对象的内存。
当以拷贝的方式初始化一个对象时,会调用一个特殊的构造函数,就是拷贝构造函数(Copy Constructor)。
定义与使用
以拷贝的方式初始化一个对象时,会调用一个特殊的构造函数,就是拷贝构造函数(Copy Constructor)。
我们先看拷贝构造函数的声明
1 2 3 4 5
|
Student(const Student &stu);
|
我们接着看拷贝构造函数的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
Student::Student(const Student &student) { cout << ">>>>>调用类的拷贝构造函数" << endl; this->stuID = student.stuID; this->stuAge = student.stuAge; this->stuName = student.stuName; this->totalScore = student.totalScore; }
|
我们可以看到。拷贝构造函数只有一个参数,它的类型是当前类的引用,而且一般都是 const 引用。
为什么参数是当前类的引用呢?
如果拷贝构造函数的参数不是当前类的引用,而是当前类的对象,那么在调用拷贝构造函数时,会将另外一个对象直接传递给形参,这本身就是一次拷贝,会再次调用拷贝构造函数,然后又将一个对象直接传递给了形参,将继续调用拷贝构造函数……这个过程会一直持续下去,没有尽头,陷入死循环。
只有当参数是当前类的引用时,才不会导致再次调用拷贝构造函数,这不仅是逻辑上的要求,也是 C++ 语法的要求。
为什么是const引用呢。
拷贝构造函数的目的是用其它对象的数据来初始化当前对象,并没有期望更改其它对象的数据,添加 const 限制后,这个含义更加明确了。
另外一个原因是,添加 const 限制后,可以将 const 对象和非 const 对象传递给形参了,因为非 const 类型可以转换为 const 类型。如果没有 const 限制,就不能将 const 对象传递给形参,因为 const 类型不能转换为非 const 类型,这就意味着,不能使用 const 对象来初始化当前对象了。
以上面的 Student 类为例,将 const 去掉后,拷贝构造函数的原型变为:
1 2 3 4 5 6 7 8
| Student::Student(Student &stu);
const Student stu1("小明", 16, 90.5); Student stu2 = stu1; Student stu3(stu1);
|
stu1 是 const 类型,在初始化 stu2、stu3 时,编译器希望调用Student::Student(const Student &stu)
,但是这个函数却不存在,又不能将 const Student 类型转换为 Student 类型去调用Student::Student(Student &stu)
,所以最终调用失败了。
当然,你也可以再添加一个参数为 const 引用的拷贝构造函数,这样就不会出错了。换句话说,一个类可以同时存在两个拷贝构造函数,一个函数的参数为 const 引用,另一个函数的参数为非 const 引用。
默认拷贝构造函数
在前面的教程中,我们还没有讲解拷贝构造函数,但是却已经在使用拷贝的方式创建对象了,并且也没有引发什么错误。这是因为,如果程序员没有显式地定义拷贝构造函数,那么编译器会自动生成一个默认的拷贝构造函数。这个默认的拷贝构造函数很简单,就是使用“老对象”的成员变量对“新对象”的成员变量进行一一赋值,和上面 Student 类的拷贝构造函数非常类似。
对于简单的类,默认拷贝构造函数一般是够用的,我们也没有必要再显式地定义一个功能类似的拷贝构造函数。但是当类持有其它资源时,如动态分配的内存、打开的文件、指向其他数据的指针、网络连接等,默认拷贝构造函数就不能拷贝这些资源,我们必须显式地定义拷贝构造函数,以完整地拷贝对象的所有数据。
源码解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| class Student { private: std::string stuName; int stuID; int stuAge; float totalScore; protected: float englishScore, mathScore, articleScore;
public:
Student() { }
Student(int id, std::string name, int age, float score);
Student(int id, std::string name, int age);
Student(const Student &stu);
~Student() { std::cout << "=====调用类的析构函数==========" << std::endl; }
void input_data();
void show_data();
void display(); };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
|
#include <iostream> #include <cstdlib> #include "01_demo_object_oriented.h"
using namespace std;
Student::Student(int id, string name, int age, float score) { cout << ">>>>>调用类的含参构造函数" << endl; stuID = id; stuAge = age; stuName = name; totalScore = score; }
Student::Student(int id, string name, int age) : stuID(id), stuAge(age), stuName(name) { cout << ">>>>>调用类的直接进行变量赋值含参构造函数" << endl; }
Student::Student(const Student &student) { cout << ">>>>>调用类的拷贝构造函数" << endl; this->stuID = student.stuID; this->stuAge = student.stuAge; this->stuName = student.stuName; this->totalScore = student.totalScore; }
void Student::input_data() { cout << "请输入您的成绩:"; cin >> totalScore; }
void Student::show_data() { cout << "成绩是:" << totalScore << endl; }
void Student::display() { cout << stuName << "的年龄是:" << stuAge << ",成绩是:" << totalScore << endl; }
int main() {
cout << "===================普通对象实例化=================" << endl; Student stu1(10010, "张三", 18, 100); stu1.input_data(); stu1.show_data(); stu1.display();
cout << "===================指针方式类的实例化=================" << endl;
Student *stuPtr = new Student(); stuPtr->input_data(); stuPtr->show_data();
cout << "===================指针方式类的含参数构造函数实例化=================" << endl;
Student *stuParamPtr = new Student(1001, "李四", 20, 596); stuParamPtr->input_data(); stuParamPtr->show_data();
cout << "===================通过类的拷贝构造函数类进行实例化=================" << endl; Student student4 = *stuParamPtr; student4.display();
return 0;
}
|