一是构造函数,在对象创建时自动调用,用以完成对象成员变量等的初始化及其他操作(如为指针成员动态申请内存空间等);如果程序员没有显式的定义它,系统会提供一个默认的构造函数。
另一个是析构函数,在对象撤销时自动调用,用以执行一些清理任务,如释放成员函数中动态申请的内存等。如果程序员没有显式的定义它,系统也会提供一个默认的析构函数。
当对象被创建时,构造函数自动被调用。
构造函数有一些独特的地方:函数的名字与类名相同,没有返回类型和返回值,即使是void也不能有。
1>其主要工作有: 给对象一个标识符。
2>为对象数据成员开辟内存空间。
3>完成对象数据成员的初始化(函数体内的工作,由程序员完成)。
上述3点也说明了构造函数的执行顺序,在执行函数体之前,构造函数已经为对象的数据成员开辟了内存空间,这时,在函数体内对数据成员的初始化便是顺理成章了。 备注代码给出了point类的显式构造函数 。
class point { private: int xPos; int yPos; public: point(); }; point::point() { xPos = 0; yPos = 0; }
构造函数可以有参数:
编译器自动生成的缺省(默认)构造函数是无参的,实际上,构造函数可以接收参数,在对象创建时提供更大的自由度,如下列代码
一旦用户定义了构造函数,系统便不再提供默认构造函数。 跟踪执行,理解构造函数的执行顺序。
//point.h #include <iostream> using namespace std; class point //point类定义,在定义同时实现其成员函数 { private: //私有成员,分别代表x轴和y轴坐标 int xPos; int yPos; public: point(int x, int y) //有参构造函数 { cout << "对象创建时构造函数被自动调用" << endl; xPos = x; yPos = y; } void print() //输出信息 { cout << "xPos: " << xPos << ",yPos: " << yPos << endl; } }; #include "point.h" int main() { // point pt0;//错误的调用,因为我们已经显示的定义了一个带参数的构造函数 // pt0.print();//输出pt0的信息 point pt1(3, 4); //调用有参构造函数声明point类变量(类对象)pt1 pt1.print(); //输出pt1的信息 return 0; }
构造函数能够进行重载:
一旦程序员为一个类定义了构造函数,编译器便不会为类自动生成缺省构造函数,
因此,如果还想使用无参的构造函数,如“point pt1;”的形式必须在类定义中显式定义一个无参构造函数。这样,构造函数就会出现两个,会不会有问题呢?不会,构造函数支持重载,在创建对象时,根据传递的具体参数决定采用哪个构造函数。
例1:
#include <iostream> using namespace std; class Point { public: Point()//构造函数能够进行重载 { cout << "Point()" << endl; } Point(int ix, int iy) { cout << "Point(int,int)" << endl; _ix = ix; _iy = iy; } void print() { cout << "(" << _ix<< "," << _iy<< ")" << endl; } private: int _ix; int _iy; }; int main(void) { Point p1;//调用默认构造函数 p1.print(); Point p2(3, 4); p2.print(); return 0; }
//point1.h #include <iostream> using namespace std; class point //point类定义,在定义同时实现其成员函数 { public: point(int x, int y)//有参构造函数 { cout << "有参构造函数的调用" << endl; xPos = x; yPos = y; } point() //无参构造函数 { cout << "无参构造函数的调用" << endl; xPos = 0; yPos = 0; } void print()//输出信息 { cout << "xPos: " << xPos << ",yPos: " << yPos << endl; } private: //私有成员,分泌诶代表x轴和y轴坐标 int xPos; int yPos; }; #include "point1.h" int main() { point pt1(3, 4); //调用有参构造函数声明point类变量(类对象)pt1 pt1.print(); //输出pt1的信息 point pt2; //调用无参构造函数声明point类变量(类对象)pt2 pt2.print(); //输出pt2的信息 return 0; }
------------------
构造函数允许按参数缺省方式调用
上例中的构造函数可以作如下定义:
point(int x=0,int y=0) { cout<<"对象创建时构造函数被自动调用"<<endl; xPos=x; yPos=y; }
此时,可在创建对象时省略参数,下列语句都是合法的:
point pt; //x和y都采用默认值0 point pt(3); //x为3,y采用默认值0 point pt(3,4);//x为3,y为4,两个参数都不采用默认值
初始化表达式1
除了在构造函数体内初始化数据成员外,还可以通过成员初始化表达式来完成。
成员初始化表可用于初始化类的任意数据成员(后面要介绍的static数据成员除外),
该表达式由逗号分隔的数据成员表组成,初值放在一对圆括号中。
只要将成员初始化表达式放在构造函数的头和体之间,并用冒号将其与构造函数的头分隔开,便可实现数据成员表中元素的初始化
point(int x,int y)
{
cout<<"有参构造函数的调用"<<endl;
xPos=x;
yPos=y;
}
//等价于:
point(int x,int y)
:xPos(x)
,yPos(y)
{
cout<<"有参构造函数的调用"<<endl;
}
--------------------------
初始化表达式2
每个成员在初始化表中只能出现一次
初始化的顺序不是由成员变量在初始化表中的顺序决定的,
而是由成员变量在类中被声明时的顺序决定的。(即在private中声明的顺序)
理解这一点有助于避免意想不到的错误。
//point.h #include <iostream> using namespace std; class point { private: int yPos; //先定义 int xPos; //后定义 public: point(int x) :xPos(x) , yPos(xPos) //初始化表取决于成员声明的顺序 //如果换成 //:yPos(y) //,x(yPos)//这样的话,x先初始化,但是这时yPos还没有初始化,x就是不确定的值了 { } void print() { cout << "xPos: " << xPos << ", yPos: " << yPos << endl; } }; #include "point.h" int main() { point pt1(3); //调用有参构造函数声明变量pt1 pt1.print(); return 0; }
例子:
#include <iostream> using namespace std; class Point { public: #if 0 Point()//构造函数能够进行重载 { cout << "Point()" << endl; } #endif Point(int ix, int iy) : _ix(ix)//初始化列表 , _iy(iy) { cout << "Point(int,int)" << endl; //_ix = ix;//赋值 //_iy = iy; } void print() { cout << "(" << _ix << "," << _iy << ")" << endl; } private: int _ix; int _iy; }; int main(void) { Point p1;//调用默认构造函数 p1.print(); Point p3(5); p3.print(); Point p2(3, 4); p2.print(); return 0; }