必备技能8.5:带有参数的构造函数
在前面的例子中,我们用到的是没有参数的构造函数。在某些情况下,这样做是可以的。但是大多数情况下我们都需要带有一个或者多个参数的构造函数。为类的构造函数增加参数和为普通的函数增加参数的方式是一样的:只需要在函数名称后面的括号中声明这些参数即可。例如,下面就是类MyClass的一个带有参数的构造函数:
MyClass::MyClass(int i )
{
x = i;
}
那么如何为类的构造函数传入参数了?我们只需要在生成类的对象的时候把传入的值和该对象关联即可。C++提供了两种方式来完成这件事情。第一种方式如下:
MyClass ob1 = MyClass(101);
上面的代码创建了MyClass的一个对象,并为其传入101这个值。然而这种方式实际中用的比较少。因为第二种方式更简洁明了。其第二种方式就是把一个或者多个参数放置在对象名称的后面,并用括号括起来。例如,用第二种方法完成上面功能的写法:
MyClass ob1(101);
这是我们最常使用的方式。因此我们可以总结出给类的构造函数传递参数的一般形式为:
类类型 变量名称(参数列表);
其中的参数列表就是传递给构造函数的用逗号间隔开的一系列参数。
注意:从技术上来讲,上面提到的两种方式是有细小差别的。这点我们在后面会讨论到。然而,这些些微的差别不影响我们目前编写的程序,或者说对于我们写的大部分程序来说是没用影响。
下面就是一个完成的演示带有参数的构造函数的程序:
//带有参数的构造函数
#include <iostream>
using namespace std;
class MyClass
{
public:
int x;
//声明类的构造函数和析构函数
MyClass(int i); //构造函数
~MyClass(); //析构函数
};
//实现类的构造函数
MyClass::MyClass(int i)
{
x = i;
}
//实现类的析构函数
MyClass::~MyClass()
{
cout << "Destructing object whose x value is " << x << "\n";
}
int main()
{
//给构造函数传入参数
MyClass t1(5);
MyClass t2(19);
cout << t1.x << " " << t2.x << "\n";
return 0;
}
上面程序的输出如下:
5 19
Destructing object whose x value is 19
Destructing object whose x value is 5
在这个版本的程序中,MyClass函数的构造函数需要一个参数i。这个i被用来对实例变量x进行初始化。因此,当
MyClass ob1(5);
执行后,就是把5传递给了i,然后把这个值赋值给x。
和构造函数不同,析构函数不能含有参数。这点很容易理解:我们没有方式能够传递参数给即将被销毁的对象。如果我们确实需要在销毁对象之前为其传入一些数据(这种情况是非常罕见的),我们就应该为此创建一个特殊的变量,并在销毁对象之前,设置一下这个变量即可。
为Vehicle类的增加构造函数
到此,我们就可以通过给我们前面学过的Vehicle类增加一个带有参数的构造函数来进一步改进这个类了。通过带有参数的构造函数,可以在对象被创建的时候就对其变量passengers,uelcap和mpg进行初始化。请特别注意我们是如何创建Vehicle类的对象的。
//为Vehicle类增加构造函数
#include <iostream>
using namespace std;
//声明Vehicle类
class Vehicle
{
public:
int passengers; // 载客人数
int fuelcap; // 油箱容量
int mpg; // 每加仑油能跑的里程数
//构造函数
Vehicle(int p, int f, int m);
int range(); //计算并返回汽车能跑的最大里程数,此处为函数声明
};
//构造函数的实现
Vehicle::Vehicle(int p, int f, int m)
{
passengers = p;
fuelcap = f;
mpg = m;
}
//range()函数的实现
int Vehicle::range()
{
return mpg * fuelcap;
};
int main()
{
//通过构造函数为类的对象传递信息
Vehicle minivan(7,16,21);
Vehicle sportscar(2,14,12);
int range1, range2;
//计算汽车加满油后能跑的最大里程数
range1 = minivan.range();
range2 = sportscar.range();
cout << "Minivan can carry " << minivan.passengers << " with a range of " << range1 << "\n";
cout << "Sportscar can carry " << sportscar.passengers << " with a range of " << range2 << "\n";
return 0;
};
在上面的程序中,对象minvan和sportscar都是在创建的时候通过构造函数来初始化的。每个对象的初始化值都是通过构造函数的参数来传递的。例如:
Vehicle minivan(7,16,21);
就是把7,16,21传递给Vehicle的构造函数。因此,minivan这个对象的passengers,fuelcap,mpg的就分别是7,16和21。因此上面程序的输出和先前程序的输出时一样的。
另外一种进行初始的方式
如果一个构造函数只是需要一个参数,此时我们可以采用另外一种方式来对其进行初始化。参见下面的程序:
//另外一种进行初始化的方式
#include <iostream>
using namespace std;
class MyClass
{
public:
int x;
//声明类的构造函数和析构函数
MyClass(int i); //构造函数
~MyClass(); //析构函数
};
//实现类的构造函数
MyClass::MyClass(int i)
{
x = i;
}
//实现类的析构函数
MyClass::~MyClass()
{
cout << "Destructing object whose x value is " << x << "\n";
}
int main()
{
MyClass ob = 5;
cout << ob.x << "\n";
return 0;
}
在这里,MyClass类的构造函数只需要一个参数。请特别注意在main()函数中是如何声明ob这个对象的:
MyClass ob = 5;
在这种初始化方式下,5会自动地被传递给MyClass()构造函数的参数i。编译器对这种初始化的方式就像是和
MyClass ob(5);
一样的。
通常,在任何构造函数只有需要一个参数的情况下,我们都可以或者使用ob(x)或者使用ob=x的方式来对对象进行初始化。这是因为我们创建的构造函数如果只需要一个参数,就意味着我们阴隐式地创建了一种从参数的类型到该类类型的转换。
练习
1. 假设有一个类Test,写出如何为其声明接受一个名称为count的参数的构造函数?
2. 下面的语句可以怎么被重写:
Test ob = Test(10);
3. 上面第二个问题中的声明可以怎样被重写?