目录
委托构造函数
● 一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程, 在委托构造函数内,成员初始值列表只有一个唯一的入口, 就是类名本身(就是同类的其他构造函数的类名)。 类名后面的参数列表必须与类中另外一个构造函数匹配。
class Sales_data
{
friend istream &read(istream &is, Sales_data &item);
public:
//非委托构造函数使用对应的实参初始化成员
Sales_data(const string &s, unsigned cnt, double price) :bookNo(s), units_sold(cnt), revenue(price*cnt) {}
//其余构造函数全都委托给另外一个构造函数
Sales_data() : Sales_data("", 0, 0) {}
Sales_data(const string &s) : Sales_data(s, 0, 0) {}
Sales_data(istream &is) : Sales_data()
{
read(is, *this);
}
private:
//数据成员
string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
istream &read(istream &is, Sales_data &item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}
在这个 Sales_data 类中,除了第一个构造函数外其他的都委托了它们的工作。第一个构造函数接受三个实参,使用这些实参初始化数据成员,然后结束工作。我们定义默认构造函数令其使用三个参数的构造函数完成初始化过程, 它也无须执行其他任务, 这一点从空的构造函数体能看得出来。接受一个string的构造函数同样委托给了个三参数的版本。
接受 istream& 的构造函数也是委托构造函数,它委托给了默认构造函数, 默认构造函数又接着委托给三个参数构造函数的版本。当这些受委托的构造函数执行完后, 接着执行 istream&构造函数体的内容。它的构造函数体调用read函数读取给定的istream.
注意: 当一个构造函数委托给另一个构造函数时,受委托的构造函数的初始值列表和函数体被依次执行。 在 Sales_data
类中, 受委托的构造函数体恰好是空的。 假如函数体包含代码的话,将先执行这些代码, 然后控制权才会交还给委托者的函数体。
在贴一段完整的代码感受一下:
class Date
{
public:
//非委托构造函数使用对应的实参初始化成员
Date(int year, int month, int day)
:_year(year)
, _month(month)
, _day(day)
{
cout << "带三个参数的构造函数被调用!" << endl;
}
//其余构造函数全都委托给另一个构造函数
Date() :Date(1990, 1, 1)
{
cout << "默认构造函数被调用!" << endl;
}
Date(int year) :Date()
{
cout << "带一个构造函数被调用!" << endl;
}
void show()
{
cout << "分别输出它们的值:" << _year << " " << _month << " " << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date myDate;
myDate.show();
cout << endl;
Date myDate1(2018);
myDate1.show();
cout << endl;
Date myDate2(2018,6,21);
myDate2.show();
system("pause");
return 0;
}
输出结果为:
带三个参数的构造函数被调用!
默认构造函数被调用!
分别输出它们的值:1990 1 1
带三个参数的构造函数被调用!
默认构造函数被调用!
带一个构造函数被调用!
分别输出它们的值:1990 1 1
带三个参数的构造函数被调用!
分别输出它们的值:2018 6 21
注意: 不要递归委托
class A
{
private:
int i = 5;
string str = "初始值";
public:
A(string ss) :A(555) //错误,这里委托给了int型参数的构造函数,形成递归委托,这样就会在两个函数中来会执行,形成死循环(死递归)的构造函数
{
str = ss;
}
A(int ii) :A("OK") //错误,这里委托给了string型参数的构造函数,形成递归委托
{
//不能写成AA(int ii):A(),i(ii)
//委托构造函数不能再利用初始化器初始化其他数据成员
i = ii;
}
void show() {
cout << "i=" << i << ",str=" << str << endl;
}
};
int main()
{
A a(10);
a.show();
}
注意 : 在使用委托构造函数时,不能再进行成员列表初始化,而只能在函数体内进行初始化其他成员变量。那么我们在委托其他构造函数构造对象时,一些成员变量以默认值构造了,在函数体内进行初始化就不叫初始化了,只能叫重新赋值了。
class A
{
private:
int a;
int b;
char c;
char d;
public:
A(int num0, int num1, char C) :a(num0), b(num1), c(C) {}
A(int num0, char C) :A(num0, 0, C) {}//b默认初始化为0
A(int num0) :A(num0, 'p') { b = 1; }//b重新赋值为1
void getMembers()
{
cout << a << " " << b << " " << c << " " << d << endl;
}
};
在委托第二个构造函数构造时,b被初始化为0,这里我们在函数体内重新赋值为1,那么b到底是0还是1呢?结果是1。函数体内的初始化要晚于成员列表初始化,即委托其他构造函数构造完后,在进行函数体内的赋值。
注意 : 一个构造函数想要委托另一个构造函数,被委托的构造函数应该包含最大数量的参数,初始化较多的成员变量。被委托的构造函数并不用包含所有成员变量(没有包含的成员变量,以默认值进行初始化),然后被其他构造函数委托。