在C++面向对象程序设计中,通过构造函数对对象进行初始化,它可以为对象在计算机内存中开辟内存空间,也可以为对象的数据成员提供初始值。构造函数是一个与类同名,没有返回值的特殊成员函数,每当创建一个对象时(包括使用new动态创建对象),编译系统就会自动调用构造函数。构造函数象类以外的一般函数和类成员函数一样可以重载和带缺省参数,构造函数的重载为对象的生成提供了各种灵活的手段。构造函数分为缺省构造函数(默认构造函数)和用户自定义构造函数。当程序员没有定义构造函数时,系统会提供一个无参的缺省构造函数。如果用户自定义了一个构造函数,编译器提供的缺省构造函数就自动消失了。
首先用提问的方法复习“构造函数”的教学内容。构造函数的作用是在对象创建时对对象初始化,完成实例化一个类的具体过程。如果在一个类中不显式定义构造函数,C++编译器会自动为该类产生一个缺省的构造函数,当创建对象时,被自动执行。
有一个类Point,定义如下:
class Point
{public:
Point(int x=0,int y=0) { cout<<"Point(int,int)..."<<endl; this->x=x; this->y=y; }
void print() { cout<<x<<","<<y<<endl; }
private:
int x,y;
};
void main(void)
{ Point p1(1,1),p3(p1); p3.print(); }
提问:
(1)Point p1(1,1) 是否调用构造函数?程序运行会产生什么样的结果?
(2)以前讲过“int a(5);”相当于“int a=5;”,是不是“Point p3(p1);”也相当于“Point p3=p1;”?
(3)“Point p3(p1);”是否调用了构造函数“Point(int x=0,int y=0)”?对象创建时必定要调用构造函数,如果没有调用上面的构造函数,它又调用了哪个缺省的函数?
通过上述的问题引出拷贝构造函数的定义和功能。“Point p3(p1);”调用的是缺省的拷贝构造函数“Point (const Point &)”,它起到的作用是用已经存在的对象p1来构造并初始化一个新的对象p3,首先在内存中给p3分配空间,然后把p1中逐域的值拷贝给p3。因为在调用拷贝构造函数时不允许修改已经存在的对象中成员变量的值,所以要加上一个关键字“const”;且是引用调用,而减少了内存的占用。
每个类中如果不显式地定义拷贝构造函数,都会有一个缺省的拷贝构造函数,是不是拷贝函数只是在用已有的对象初始化新对象时调用,其它情况就用不着了呢?为了验证这个问题,请同学们想想办法,如何改写程序来达到验证的目的?
对了,加上显式的拷贝构造函数就可以了。这个函数的功能除了要完成对象之间逐域的拷贝,要加上输出语句,就可以观察到什么时候调用了拷贝构造函数。
Point(const Point &p) {cout<<"Point(const &Point)"<<endl; x=p.x; y=p.y; }
什么时候调用拷贝构造函数
显式定义了拷贝构造函数,程序中再用到用已有的对象初始化新对象时,就调用这个函数。还有什么情况下可能调用这个函数呢?
举例:增加一个运算符“+”的重载函数,完成两个对象之间相加的运算。
Point operator +(Point p1,Point p2)
{ Point P3; P3.x=p1.x+p2.x; P3.y=p1.y+p2.y; return P3; }
在main()函数时进行两个对象相加的运算:
void main(void) { Point p1(1,1),p2(2,2),p3; p3=p1+p2; p3.print(); }
程序运行时,拷贝构造函数被调用了3次,而程序中却没有用已有对象对新对象进行初始化的语句,哪什么时候调用了拷贝构造函数呢?
这时可以提醒学生增加函数中的输出信息来判断。
将拷贝构造函数改为:
Point(const Point &p)
{ cout<<"Point(const &Point) x="<<p.x<<" y="<<p.y<<endl; x=p.x; y=p.y; }
程序的运行结果变为:
Point(1,1)...
Point(2,2)...
Point(0,0)...
Point(const &Point) x=2 y=2
Point(const &Point) x=1 y=1
Point(0,0)...
Point(const &Point) x=3 y=3
3,3
让学生分析结果得到结论:当对象作为形参时和对象作为返回值时调用了拷贝构造函数。马上以提出新的问题:是不是所有的拷贝函数只写出逐域拷贝就可以了呢?由此引出浅拷贝和深拷贝的内容。