定义构造函数的四种方法
定义构造函数的四种方法 作者:lyb661
定义类的构造函数有如下几种方法:
1、使用默认构造函数(类不另行定义构造函数):能够创建一个类对象,但不能初始化类的各个成员。
2、显式定义带有参数的构造函数:在类方法中定义,使用多个参数初始化类的各个数据成员。
3、定义有默认值的构造函数:构造函数原型中为类的各个成员提供默认值。
4、使用构造函数初始化列表:这个构造函数初始化成员的方式显得更紧凑。
例如:有一个学生类。其中存储了学生的姓名、学号和分数。
class Student
{
private:
std::string name;
long number;
double scores;
public:
Student(){}//1:default constructor
Student(const std::string& na,long nu,double sc);
Student(const std:;string& na="",long nu=0,double sc=0.0);
Student(const std:;string& na="none",long nu=0,double sc=0.0):name(na),number(nu),scores(sc){}
………..
void display() const;
//void set(std::string na,long nu,double sc);
};
.........
Student::Student(const std::string& na,long nu,double sc)
{
name=na;
number=nu;
scores=sc;
}
void Student::display()const
{
std::cout<<"Name: "<<name<<", Number: "<<number<<", Score: "<<scores<<"\n\n";
}
这个学生类极为简单:除了创建和显示学生的姓名、学号、学分以外;什么工作也不做,什么工作也做不了。
这个类在此处的价值的就是说明类构造函数的工作方式。
1、第一个构造函数,就是编译器合成的默认构造函数。它用于创建一个类对象而不显式地初始化各个类成员。
Student(){}
................
Student st1;
这里创建一个学生类的对象st1。但对st1的姓甚名谁、学号大小、学分多少一无所知。
对于一个简单的类:我们无需显式地定义这个构造函数,因为编译器会隐式地调用它来创建类对象。为了创建一个具体的类对象,有时可定义一个私有成员函数来初始化类的各个成员。
比如Set_name(string) , Set_score(double)…..
2、第二个构造函数带有三个参数:分别对应于三个类成员。经过类方法中的定义,我们可以在用户程序中创建一个各个成员赋有初值的类对象。
Student(const string& na,long nu,double sc);
................
Student st2("LiuYang",0803,96.5);
这里创建了一个学生类st2。他的姓名、学号、学分分别为LiuYang , 0803 , 96.5。
值得注意的是:由于我们显式地定义了一个带有参数的构造函数,编译器合成的默认构造函数将不再工作。因此,在用户程序中声明一个没有成员值的学生类对象就不允许了。即下面的代码是非法的!
Student st2;
3、对于第二个构造函数的问题,我们有两种方法来解决:一是同时显式地调用默认的构造函数。即第1 第 2个构造函数同时调用。这样既可创建一个类对象,也能对其进行初始化。
另一个办法是定义一个参数带有默认值的构造函数。它兼有第1 第 2个构造函数的功能。即创建一个类对象,同时为它赋初值。
Student(const std::string& na="none,long nu=0,double sc=0.0);
................
Student st3;//实际上st3也是有默认值的了
st3=Student("LiMing",0705,88.3);
Student st3("LiMing",0705,88.3); //同理
在所有类型的构造函数中,带有默认值的构造函数最为实用,也最为简洁。
4、正因为此,构造函数初始化列表的应用才被广泛地应用。
Student(const std:;string& na="none",long nu=0,double sc=0.0):name(na),number(nu),scores(sc){}
不但因为它是带有默认参数的构造函数,也因为它初始化成员的方式最为直观,最为简洁。
这使构造函数也好像被封装在类声明里了。尽管构造函数初始化列表看起来简单,其实也能完成复杂的初始化工作。
例如有一个商品类:
class ShangPin
{
std::string PM;//品名
long SL;//数量
double DJ;//单价
double JE;//金额
…!…
};
其成员有品名、数量、单价、金额。显然金额的初始化不是直接完成的,只有数量和单价已知后它才能确定。尽管工作有点麻烦,但初始化列表也能胜任。
可以在大括号中,调用一个私有的函数来完成金额的初始化工作。
ShangPin(const std::string& m_PM="nothing",long m_SL=0,double m_DJ=0.0):PM(m_PM),SL(m_SL),DJ(m_DJ){set_JE();}
................
void ShangPin::set_JE()
{
JE=SL*DJ;
}
类成员的初始化工作是十分重要的。有时我们编写好的程序,在运行时会输出些莫名其妙的乱码,这可能与我们初始化工作做不到位有关。
根据程序对类的需求,我们应该更清晰地选择构造函数的编写方法。对于一般的类,构造函数初始化列表通常是最好的选择。
当然,我们必须必须熟悉各种构造函数的工作方式,才能作出最恰如其分的选择。