1、先看这个例子:
#include <iostream>
#include <string>
using namespace std;
class Student{
private:
string name;
int age;
public:
Student(){ name = "Jerry"; age = 20; };//构造函数
Student(string strName, int nAge) :name(strName), age(nAge){}//带参数的构造函数
~Student(){};
void show()
{
cout << name << ":" << age << endl;
}
};
int main()
{
Student st1("Tom", 21);
Student st2(st1);
st2.show();
Student st3 = st1;
st3.show();
return 0;
}
运行结果是:
2、分析
其中下面两个语句分别是对象的“复制”和“赋值=”:
Student st2(st1);//复制构造函数(拷贝构造函数)
Student st3 = st1; // 赋值
在给对象赋值时候其实也是调用的复制构造函数,那么这个复制构造函数在哪里?类中并没有定义,其实在对象st2创建的时候,系统自动生成了一个复制构造函数,就像类中没有定义默认构造函数,在对象创建的时候会自动创建一个默认构造函数一样。
复制构造函数的作用就是把目标对象的所有成员赋值给自己的所有成员,这里的成员都是简单成员。复制构造函数的形式如下:
ClassName (const ClassName& obj)
参数是该类的一个对象的引用,是个常量,为了防止在复制时更改目标对象;返回值是一个该类的对象。
上面的例子在执行Student st2(st1);的时候其实就是 让st2.name = st1.name;st2.age = st1.age;
上面所说的简单成员,其实就是不能包括动态分配的数据。如果Student类中还有一个类似下面的私有成员:
Course *pCourse;
其中,关联如下的结构体:
struct Course
{
int nCourseId;//课程号
double score;//成绩
};
那么pCourse需要先在对象构造的时候动态分配内存。
改动程序如下所示:
struct Course
{
int nCourseId;
double score;
};
class Student{
private:
string name;
int age;
Course *pCourse;
public:
Student()//构造函数
{
name = "Jerry";
age = 20;
pCourse = new Course;
pCourse->nCourseId = 1001;
pCourse->score = 60.0;
};
Student(string strName, int nAge,int CourseId,double dScore)//带参数的构造函数
{
pCourse = new Course;
name = strName;
age = nAge;
pCourse->nCourseId = CourseId;
pCourse->score = dScore;
}
~Student(){ delete pCourse; }
void show()
{
cout << name << ":" << age << endl;
cout << pCourse->nCourseId << ":" << pCourse->score << endl;
}
};
int main()
{
Student st1("Tom", 21,1003,89.5);
Student st2(st1);
st2.show();
Student st3 = st1;
st3.show();
return 0;
}
此时运行报错。因为Student类中包含了需要动态分配内存的pCourse 成员。
其实自定义一个复制构造函数就可以实现这种复杂对象的复制。添加如下复制构造函数:
Student(const Student &st)//复制构造函数
{
pCourse = new Course;
name = st.name;
age = st.age;
pCourse->nCourseId = st.pCourse->nCourseId;
pCourse->score = st.pCourse->score;
}
可以正常运行,运行结果:
3、对象复制和对象赋值的区别
(1)对象复制:用已有对象初始化新的对象。
(2)对象赋值:就是已经定义了两个对象,把一个对象的成员 赋值给另一个对象的成员。
4、如果说对象赋值是先定义好一个对象,比如说st3,那么st3的成员pCourse 已经被new过一次,如果再通过st3 = st1的时候相当于调用了复制构造函数(Student (const Student & st));其中又对 pCourse 进行了一次new 。
虽然运行正常,但是感觉还是不对。
谭浩强《C++程序设计 》p293 页中指出:“类的数据成员中不能包括动态分配的数据,否则在赋值的时候可能出现严重的后果。”