3.2构造函数
Part1.应用场景
在对象被创建时利用特定的值构造对象,将对象初始化为一个特定的状态。
Part2.定义及代码
3.2.1构造函数、默认构造函数
1)定义:在定义对象的时候进行的数据成员设置,称为对象的初始化。
构造函数的作用就是在对象被创建时利用特定的值构造对象,将对象初始化为一个特定的状态。
2)性质:
①构造函数的函数名与类名相同;
② 构造函数没有返回值;
③通常被声明为公有函数;
④构造函数在对象被调用的时候自动创建。
3)默认构造函数:
调用时无需提供参数的构造函数称为默认构造函数。
如果类中没有写构造函数,编译器会自动生成一个隐含的默认构造函数,该构造函数的参数列表和函数体皆为空。
如果声明了构造函数(无论是否有参数),编译器都不会在为之生成隐含的构造函数。
例上题:
class Clock{ public: Clock(){} //编译系统生成的隐含的默认构造函数 ... };
这个构造函数不作任何事。
自己定义的构造函数:
class Clock { public: Clock(int newH,int newM,int newS);//构造函数,函数名与类名相同,且没有返回值类型 void setTime(int newH = 0, int newM = 0, int newS = 0); void showTime(); private: int hour, minute, second; };
4)构造函数的实现
Clock::Clock(int newH, int newM, int newS) { //自定义的构造函数带有形参,那么建立对象时就必须给出初值 hour = newH; minute = newM; second = newS; }
自定义的构造函数带有形参,那么建立对象时就必须给出初值
int main() { Clock myClock(0,0,0);//自定义的构造函数带有形参,建立对象时必须给出参数值 myClock.showTime(); myClock.setTime(8, 30, 30); myClock.showTime(); return 0; }
若创建对象时,没有给出参数值,就会出错。
int main() { Clock myClock;//自定义的构造函数带有形参,建立对象时没有给出参数值 myClock.showTime(); myClock.setTime(8, 30, 30); myClock.showTime(); return 0; }
注意:
构造函数可以直接访问类的所有数据成员,可以使内联函数,可以带有参数表,可以带默认的形参值,也可以重载。
构造函数的重载及被调用情况:
class Clock { public: Clock(int newH,int newM,int newS);//带形参的构造函数 Clock(){ //无参数的构造函数 hour=newH; minute=newM; second=newS; } void setTime(int newH = 0, int newM = 0, int newS = 0); void showTime(); private: int hour, minute, second; };
int main() { Clock c1(0,0,0);//调用有参数的构造函数 Clock c2;//调用无参数的构造函数 ... }
3.2.2复制构造函数
①定义:复制构造函数是一种特殊的构造函数,具有一般构造函数的所有特性,其形参是本类的对象的引用。
②作用:使用一个已经存在的对象(由复制构造函数的参数指定),去初始化同类的一个新对象。
③隐含的复制构造函数:如果程序员没有定义类的复制构造函数,系统就会在必要时自动生成一个隐含的复制构造函数。
其功能:把初始值对象的每个数据成员的值都复制到新建立的对象中。也可以说完成了同类对象的复制,这样的对象和原对象具有完全相同的数据成员。
④声明方法:
class 类名 { public: 类名(形参表);//构造函数 类名(类名&对象名);//复制构造函数 ... } 类名::类名(类名&对象名); { 函数体 }
实例:
class Point{ public: Point(int xx=0,int yy=0){//构造函数 x=xx; y=yy; } Point(Point &p);//复制构造函数 int getX(){return x;} int getY(){return y;} private: int x,y; }; Point::Point(Point &p){ x=p.x;//对象名. y=p.y; cout<<"Calling the copy constructor"<<endl; }
⑤复制构造函数被调用的三种情况:
a.当用类的一个对象去初始化该类的另一个对象时。
b.如果函数的形参是类的对象,调用函数时,进行形参和实参的结合时。(只有把对象用值传递时才会调用复制构造函数,如果传递运用,则不会调用)
c.如果函数的返回值是类的对象,函数执行完成返回调用者时。
⑥程序实例:
例4-2
#include<iostream> using namespace std; class Point { public: Point(int xx = 0, int yy = 0) { x = xx; y = yy; } Point(Point& p); int getX() { return x; } int getY() { return y; } private: int x, y; }; //成员函数的实现 Point::Point(Point& p) { x = p.x; y = p.y; cout << "Calling the copy constructor" << endl; } //形参为Point类对象的函数 void fun1(Point p) { cout << p.getX() << endl; } //返回值为Point类对象的函数 Point fun2() { Point a(1, 2); return a; } //主程序 int main() { Point a(4, 5);//第一个对象a Point b = a;//情况一,用a初始化b,第一次调用复制构造函数 cout << b.getX() << endl; fun1(b);//情况二,对象b作为fun1的实参,第二次调用复制构造函数 b = fun2();//情况三,函数返回值为类对象,函数返回时调用复制构造函数 cout << b.getX() << endl; return 0; }
主函数中分别列出了3种情况下复制构造函数别调用的情况。
运行结果:
3.3析构函数
Part1.应用场景
用来完成对象被删除前的一些清理工作,也就是专门的扫尾工作。
一般来讲,如果希望程序在对象被删除的时刻自动完成某些事情,就可以把他们写到析构函数中。
Part2.定义及代码
①作用:用来完成对象被删除前的一些清理工作,也就是专门的扫尾工作。
②调用:析构函数是在对象的生存期即将结束的时刻被自动调用的。
他的调用完成之后,对象也就消失了,相应的内存空间也被释放。
③注意:
他的名称是由类名前面加“~”构成,没有返回值。
和构造函数不同的是析构函数不接受任何参数,但可以是虚函数。
如果不进行显示说明,系统会生成一个函数体为空的隐含析构函数。(函数体为空的析构函数不作任何事)
⑤例如:
class Clock { public: Clock();//构造函数 void setTime(int newH = 0, int newM = 0, int newS = 0); void showTime(); ~Clock(){} //空的内联析构函数 private: int hour, minute, second; };
一般来讲,如果希望程序在对象被删除的时刻自动完成某些事情,就可以把他们写到析构函数中。
3.4程序实例
游泳池改造预算,Circle类
#include<iostream> using namespace std; const float PI = 3.141593; const float FENCE_PRICE = 35; const float CONCRETE_PRICE = 20; class Circle { public: Circle(float r);//构造函数 float circumference();//计算圆的周长 float area();//计算圆的面积 private: float radius; }; //类的实现 //构造函数初始化数据成员radius Circle::Circle(float r) { radius = r; } //计算圆的周长 float Circle::circumference() { return 2 * PI * radius; } //计算圆的面积 float Circle::area() { return PI * radius * radius; } //主函数的实现 int main() { float radius; cout << "Emter the radius of the pool:"; cin >> radius; Circle pool(radius);//游泳池边界对象 Circle poolRim(radius + 3);//栅栏对象 //计算栅栏造价并输出 float fenceCost = poolRim.circumference() * FENCE_PRICE; cout << "Fencing Cost is $" << fenceCost << endl; //计算过道造价并输出 float concreteCost = (poolRim.area() - pool.area()) * CONCRETE_PRICE; cout << "Concrete Cost is $" << concreteCost << endl; return 0; }
分析:
将PI,栅栏单价,水泥单价定义为宏常量;定义类,有成员函数:计算周长,计算面积;之后是在类外声明这两个成员函数,写出周长和面积的计算公式;
最后是主函数的实现,声明两个对象,这两个对象的关系是:对象2=对象1+3;然后通过对象调用成员函数,分别乘以各自的价格即可得到造价预算。