类与对象

# Definition

Constructor&Destructor:构造函数与析构函数

C++提供了构造函数(constructor)来处理对象的初始化。在建立对象时自动执行。构造函数的名字必须与类名同名,它不具有任何类型,不返回任何值。构造函数的功能是由用户定义的,用户根据初始化的要求设计函数体和函数参数。在类对象进入其作用域时调用构造函数。构造函数没有返回值,因此也不需要在定义构造函数时声明类型,这是它和一般函数的一个重要的不同之点。 如果构造函数不带参数,在函数体中对数据成员赋初值,使该类的每一个对象都得到同一组初值。采用带参数的构造函数,在调用不同对象的构造函数时,从外面将不同的数据传递给构造函数,以实现不同的初始化。构造函数首部的一般格式为 构造函数名(类型 1 形参 1,类型 2 形参 2,…) 实参是在定义对象时给出的。定义对象的一般格式为类名 对象名(实参 1,实参 2,…);

include

using namespace std; class Box { public: Box(int,int,int); int volume(); private: int height; int width; int length; }; Box::Box(int h,int w,int len) { height=h; width=w; length=len; } int Box::volume() { return(heightwidthlength); } int main( ) { Box box1(12,25,30); cout<<"the volume of box1 is"<<box1.volume()<<endl; Box box2(15,30,21); cout<<"the volume of box2 is"<<box2.volume()<<endl; return 0; }

参数初始化列表

C++还提供另一种初始化数据成员的方法——参数初始化表来实现对数据成员的初始化。这种方法不在函数体内对数据成员初始化,而是在函数首部实现。 Box∷Box(int h,int w,int len):height(h),width(w),length(len){ } 构造函数具有相同的名字,而参数的个数或参数的类型不相同。这称为构造函数的重载。调用构造函数时不必给出实参的构造函数,称为默认构造函数 (default constructor)。参的构造函数属于默认构造函数。一个类只能有一个默认构造函数,尽管在一个类中可以包含多个构造函数,但是对于每一个对象来说,建立对象时只执行其中一个构造函数。

Box( ); //声明一个无参的构造函数
Box(int h,int w,int len):height(h),width(w),length(len){ }
//声明一个有参的构造函数,用参数的初始化表对数据成员初始化
Box(int h=10,int w=10,int len=10); //在声明构造函数时指定默认参数
Box::Box(int h,int w,int len)
{
height=h;
width=w;
length=len;
}

应该在声明构造函数时指定默认值,而不能只在定义构造函数时指定默认值。如果构造函数的全部参数都指定了默认值,则在定义对象时可以给一个或几个实参,也可以不给出实参。在一个类中定义了全部是默认参数的构造函数后,不能再定义重载构造函数。

#include <iostream>
using namespace std;
class Box
{public:
Box(int h=10,int w=10,int len=10); //在声明构造函数时指定默认参数
int volume( );
private:
int height;
int width;
int length;
};
Box::Box(int h,int w,int len) //在定义函数时可以不指定默认参数
{
height=h;
width=w;
length=len;
}
int Box::volume( )
{
return(height*width*length);
}
int main( )
{
Box box1; //没有给实参
cout<<"The volume of box1 is"<<box1.volume( )<<endl;
Box box2(15); //只给定一个实参
cout<<"The volume of box2 is"<<box2.volume( )<<endl;
Box box3(15,30); //只给定2个实参
cout<<"The volume of box3 is"<<box3.volume( )<<endl;
Box box4(15,30,20); //给定3个实参
cout<<"The volume of box4 is"<<box4.volume( )<<endl;
return 0;
}

析构函数

析构函数(destructor)也是一个特殊的成员函数,它的作用与构造函数相反,它的名字是类名的前面加一个“~”符号,析构函数是与构造函数作用相反的函数。当对象的生命期结束时,会自动执行析构函数:① 如果在一个函数中定义了一个对象(它是自动局部对象),当这个函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。②static 局部对象在函数调用结束时对象并不释放,因此也不调用析构函数,只在 main 函数结束或调用 exit 函数结束程序时,才调用 static 局部对象的析构函数。③ 如果定义了一个全局对象,则在程序的流程离开其作用域时(如 main 函数结束或调用 exit 函数) 时,调用该全局对象的析构函数。④ 如果用 new 运算符动态地建立了一个对象,当用 delete 运算符释放该对象时,先调用该对象的析构函数。析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作,使这部分内存可以被程序分配给新对象使用。析构函数不返回任何值,没有函数类型,也没有函数参数。因此它不能被重载。一个类可以有多个构造函数,但只能有一个析构函数。当然,析构函数也可被用来执行“用户希望在最后一次使用对象之后所执行的任何操作”,例如输出有关的信息。如果用户没有定义析构函数,C++编译系统会自动生成一个析构函数,实际上什么操作都不进行。在一般情况下,调用析构函数的次序正好与调用构造函数的次序相反:最先被调用的构造函数,其对应的(同一对象中的)析构函数最后被调用,而最后被调用的构造函数,其对应的析构函数最先被调用。

初始化

1)如果对一个类定义了两个或多个对象,则这些同类的对象之间可以互相赋值对象之间的赋值也是通过赋值运算符“=”进行的。这是通过对赋值运算符的重载实现的。

对象名 1 = 对象名 2;

对象的赋值只对其中的数据成员赋值,而不对成员函数赋值。类的数据成员中不能包括动态分配的数据,否则在赋值时可能出现严重后果。

2)用一个已有的对象快速地复制出多个完全相同的对象。如

Box box2(box1);

其作用是用已有的对象 box1 去克隆出一个新对象 box2。

其一般形式为

类名 对象 2(对象 1);

用对象 1 复制出对象 2。 通过复制建立对象时调用一个特殊的构造函数—复制构造函数(copy constructor)。形式如下:

//The copy constructor definition.

Box∷Box(const Box& b)

{height=b.height;

width=b.width;

length=b.length;

}

另一种方便用户的复制形式,用赋值号代替括号,如

Box box2=box1;//用 box1 初始化 box2

其一般形式为 类名 对象名 1 = 对象名 2;

可以在一个语句中进行多个对象的复制。如

Box box2=box1,box3=box2;

请注意普通构造函数和复制构造函数的区别。

(1) 在形式上

类名(形参表列);//普通构造函数的声明,如 Box(int h,int w,int len);

类名(类名& 对象名); //复制构造函数的声明,如 Box(Box &b);

(2) 在建立对象时,实参类型不同。系统会根据实参的类型决定调用普通构造函数或复制构造函数。如

Box box1(12,15,16); //实参为整数,调用普通构造函数

Box box2(box1); //实参是对象名,调用复制构造函数

(3) 在什么情况下被调用

普通构造函数在程序中建立对象时被调用。

复制构造函数在用已有对象复制一个新对象时被调用,在以下 3 种情况下需要克隆对象:

① 程 序中需要新建立一个对象,并用另一个同类的对象对它初始化。② 当函数的参数为类的对象时。在调用函数时需要将实参对象完整地传递给形参,系统是通过调用 复制构造函数来实现的。③ 函数的返回值是类的对象。此时需要将函数中的对象复制一个临时对象并传给该函数的调用处。