构造函数的初始化列表
语法:
CGirl(string name, int age, CBoy& boy) :m_name(name), m_age(age),m_boy(boy) // 三个参数的普通构造函数。
{
cout << "调用了CGirl(name,age,boy)构造函数。\n";
}
- 如果成员已经在初始化列表中,则不应该在构造函数中再次赋值。
- 初始化列表的括号中可以是具体的值,也可以是构造函数的形参名,还可以是表达式。
- 初始化列表与赋值有本质的区别,如果成员是类,使用初始化列表调用的是成员类的拷贝构造函数,而赋值则是先创建成员类的对象(将调用成员类的普通构造函数),然后再赋值。
- 如果成员是类,初始化列表对性能略有提升。
- 如果成员是常量和引用,必须使用初始列表,因为常量和引用只能在定义的时候初始化。
- 如果成员是没有默认构造函数的类,则必须使用初始化列表。
- 拷贝构造函数也可以有初始化列表。
- 类的成员变量可以不出现在初始化列表中。
- 构造函数的形参先于成员变量初始化。
1的例子:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CGirl // 超女类CGirl。
{
public:
string m_name; // 姓名属性。
int m_age; // 年龄属性。
CGirl():m_name("西施"),m_age(22) // 没有参数的普通构造函数,默认构造函数。
{
m_name="赋值后的西施";
cout << "调用了CGirl()构造函数。\n";
}
//CGirl(string name, int age,CBoy &boy) // 三个参数的普通构造函数。
//{
// m_name = name; m_age = age; m_boy.m_xm = boy.m_xm;
// cout << "调用了CGirl(name,age,boy)构造函数。\n";
//}
CGirl(string name, int age) :m_name(name), m_age(age) // 三个参数的普通构造函数。
{
cout << "调用了CGirl(name,age,boy)构造函数。\n";
}
// 超女自我介绍的方法,显示姓名、年龄、男朋友。
void show() { cout << "姓名:" << m_name << ",年龄:" << m_age << endl; }
};
int main()
{
CGirl g1;
g1.show();
}
2的例子:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CGirl // 超女类CGirl。
{
public:
string m_name; // 姓名属性。
const int m_age; // 年龄属性。
//CGirl() // 没有参数的普通构造函数,默认构造函数。
//{
// cout << "调用了CGirl()构造函数。\n";
//}
//CGirl(string name, int age,CBoy &boy) // 三个参数的普通构造函数。
//{
// m_name = name; m_age = age; m_boy.m_xm = boy.m_xm;
// cout << "调用了CGirl(name,age,boy)构造函数。\n";
//}
CGirl(string name, int age) :m_name(name), m_age(age+100) // 三个参数的普通构造函数。
{
cout << "调用了CGirl(name,age,boy)构造函数。\n";
}
// 超女自我介绍的方法,显示姓名、年龄、男朋友。
void show() { cout << "姓名:" << m_name << ",年龄:" << m_age << endl; }
};
int main()
{
CGirl g1("冰冰",18);
g1.show();
}
3的例子:
使用初始化列表,
- 先调用的是成员类的拷贝构造函数,
- 而赋值则是先创建成员类的对象(将调用成员类的普通构造函数),
- 然后再赋值
总结就是:先初始化构造函数的形参对象boy
(使用拷贝构造函数,从实参对象CBoy boy("子都");
拷贝过来),然后初始化类成员(使用普通构造函数初始化类成员m_boy
)
区分了(数据传递的方向是)实参–>形式参数–>类成员
拷贝构造函数
这一篇博文保姆级详细的解释了这个传参中,构造函数调用的问题
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CBoy // 男朋友类。
{
public:
string m_xm; // 男朋友的姓名。
CBoy() // 没有参数的普通构造函数,默认构造函数。
{ m_xm.clear(); cout << "调用了CBoy()构造函数。\n"; }
CBoy(string xm) // 有一个参数的普通构造函数。
{ m_xm = xm; cout << "调用了CBoy(string xm)构造函数。\n"; }
CBoy(const CBoy& bb) // 默认拷贝构造函数。
{ m_xm = bb.m_xm; cout << "调用了CBoy(const CBoy &bb)拷贝构造函数。\n"; }
};
class CGirl // 超女类CGirl。
{
public:
string m_name; // 姓名属性。
int m_age; // 年龄属性。
CBoy m_boy; // 男朋友的信息。
CGirl() // 没有参数的普通构造函数,默认构造函数。
{
cout << "调用了CGirl()构造函数。\n";
}
CGirl(string name, int age, CBoy boy) :m_name(name), m_age(age) // 三个参数的普通构造函数。
{
m_boy.m_xm = boy.m_xm;
cout << "调用了CGirl(name,age,boy)构造函数。\n";
}
// 超女自我介绍的方法,显示姓名、年龄、男朋友。
void show() { cout << "姓名:" << m_name << ",年龄:" << m_age << ",男朋友:" << m_boy.m_xm << endl; }
};
int main()
{
CBoy boy("子都");
CGirl g1("冰冰",18,boy);
g1.show();
}
将构造函数中的类形参改为引用,则不调用拷贝构造函数了
(省去了之前实参向形参传值调用的那个拷贝构造函数)
CGirl(string name, int age, CBoy& boy) :m_name(name), m_age(age) // 三个参数的普通构造函数。
{
m_boy.m_xm = boy.m_xm;
cout << "调用了CGirl(name,age,boy)构造函数。\n";
}
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CBoy // 男朋友类。
{
public:
string m_xm; // 男朋友的姓名。
CBoy() // 没有参数的普通构造函数,默认构造函数。
{ m_xm.clear(); cout << "调用了CBoy()构造函数。\n"; }
CBoy(string xm) // 有一个参数的普通构造函数。
{ m_xm = xm; cout << "调用了CBoy(string xm)构造函数。\n"; }
CBoy(const CBoy& bb) // 默认拷贝构造函数。
{ m_xm = bb.m_xm; cout << "调用了CBoy(const CBoy &bb)拷贝构造函数。\n"; }
};
class CGirl // 超女类CGirl。
{
public:
string m_name; // 姓名属性。
int m_age; // 年龄属性。
CBoy m_boy; // 男朋友的信息。
CGirl() // 没有参数的普通构造函数,默认构造函数。
{
cout << "调用了CGirl()构造函数。\n";
}
CGirl(string name, int age, CBoy& boy) :m_name(name), m_age(age) // 三个参数的普通构造函数。
{
m_boy.m_xm = boy.m_xm;
cout << "调用了CGirl(name,age,boy)构造函数。\n";
}
// 超女自我介绍的方法,显示姓名、年龄、男朋友。
void show() { cout << "姓名:" << m_name << ",年龄:" << m_age << ",男朋友:" << m_boy.m_xm << endl; }
};
int main()
{
CBoy boy("子都");
CGirl g1("冰冰",18,boy);
g1.show();
}
使用初始化列表,则:初始化类成员调用拷贝构造函数了
(之前没有使用初始化列表,初始化类成员调用普通构造函数。。)
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CBoy // 男朋友类。
{
public:
string m_xm; // 男朋友的姓名。
CBoy() // 没有参数的普通构造函数,默认构造函数。
{ m_xm.clear(); cout << "调用了CBoy()构造函数。\n"; }
CBoy(string xm) // 有一个参数的普通构造函数。
{ m_xm = xm; cout << "调用了CBoy(string xm)构造函数。\n"; }
CBoy(const CBoy& bb) // 默认拷贝构造函数。
{ m_xm = bb.m_xm; cout << "调用了CBoy(const CBoy &bb)拷贝构造函数。\n"; }
};
class CGirl // 超女类CGirl。
{
public:
string m_name; // 姓名属性。
int m_age; // 年龄属性。
CBoy m_boy; // 男朋友的信息。
CGirl() // 没有参数的普通构造函数,默认构造函数。
{
cout << "调用了CGirl()构造函数。\n";
}
CGirl(string name, int age, CBoy& boy) :m_name(name), m_age(age),m_boy(boy) // 三个参数的普通构造函数。
{
// m_boy.m_xm = boy.m_xm;
cout << "调用了CGirl(name,age,boy)构造函数。\n";
}
// 超女自我介绍的方法,显示姓名、年龄、男朋友。
void show() { cout << "姓名:" << m_name << ",年龄:" << m_age << ",男朋友:" << m_boy.m_xm << endl; }
};
int main()
{
CBoy boy("子都");
CGirl g1("冰冰",18,boy);
g1.show();
}
5的例子:
#include <iostream> // 包含头文件。
using namespace std; // 指定缺省的命名空间。
class CBoy // 男朋友类。
{
public:
string m_xm; // 男朋友的姓名。
CBoy() // 没有参数的普通构造函数,默认构造函数。
{ m_xm.clear(); cout << "调用了CBoy()构造函数。\n"; }
CBoy(string xm) // 有一个参数的普通构造函数。
{ m_xm = xm; cout << "调用了CBoy(string xm)构造函数。\n"; }
CBoy(const CBoy& bb) // 默认拷贝构造函数。
{ m_xm = bb.m_xm; cout << "调用了CBoy(const CBoy &bb)拷贝构造函数。\n"; }
};
class CGirl // 超女类CGirl。
{
public:
string m_name; // 姓名属性。
const int m_age; // 年龄属性。
CBoy& m_boy; // 男朋友的信息。
// CGirl(string name, int age, CBoy& boy) // 三个参数的普通构造函数。
// {
// m_name = name;
// m_age = age;
// m_boy.m_xm = boy.m_xm;
// cout << "调用了CGirl(name,age,boy)构造函数。\n";
// }
CGirl(string name, int age, CBoy& boy):m_name(name),m_age(age),m_boy(boy) // 三个参数的普通构造函数。
{
cout << "调用了CGirl(name,age,boy)构造函数。\n";
}
// 超女自我介绍的方法,显示姓名、年龄、男朋友。
void show() { cout << "姓名:" << m_name << ",年龄:" << m_age << ",男朋友:" << m_boy.m_xm << endl; }
};
int main()
{
CBoy boy("子都");
CGirl g1("冰冰",18,boy);
g1.show();
}
类形参改为引用,则不调用拷贝构造函数了
类成员改为引用,则不调用拷贝构造函数了(使的是初始化列表)
如果都不使用引用,则是如下的结果: