构造函数
类对象构造顺序
类对象的构造顺序是这样的:
1.分配内存,调用构造函数时,隐式/显示的初始化各数据成员(构造函数列表的初始化方式不是按照列表的的顺序,而是按照变量声明的顺序同时初始化显隐数据成员);
2.进入构造函数后在构造函数中执行一般赋值与计算。
例子1
class A {
public:
int x;
const int y;
int& z;
A() :y(1), z(x), x(0) { x = 100; }
};
void main() {
A a;
cout << "a.x:" << a.x << " a.y:" << a.y << " a.z:" << a.z << endl;
}
执行顺序为:
- 初始化x(0) 初始化y(1) 初始化z(x)
- 执行构造函数体,赋值x=100;
例子2
这个例子在网上很多博客都有,但是发现他们的解释全都错了!
class Weapon
{
private:
string name;
const string type;
const string model;
public:
Weapon(string& name, string& type, string& model) :name(name), type(type), model(model)
{
name = "Cloud";
}
string getProfile()
{
return "name: " + name + "
type: " + type + "
model: " + model;
}
};
int main(void)
{
string name = "Comet";
string type = "carbine";
string model = "rifle";
Weapon weapon(name, type, model);
cout << weapon.getProfile() << endl;
}
执行后的结果是
可以发现这和我们预期的是不一样的,不是应该name:Cloud吗??
网上其他的博客对此做出的解释“是注意观察,构造函数里的 name = "Cloud"; 被初始化列表的值覆盖了”
嗯..这其实是瞎扯淡..
再理一下顺序
- 执行初始化表
name(name), type(type), model(model)
- 执行构造函数体
name = "Cloud";
问题的所在就是构造函数体中的name
其实是参数,而非Weapon类中的成员变量。我们稍作改变
Weapon(string& nam, string& type, string& model) :name(nam), type(type), model(model)
{
name = "Cloud";
}
发现这时候答案就和想象中的一样了
成员初始化表和构造函数体
成员的初始化列表和构造函数在对成员指定初值方面是不一样的。成员初始化列表是对成员初始化,而构造函数,是对成员赋值
- 成员初始化列表使用初始化的方式来为数据成员指定初值,
- 而构造函数的函数体是通过赋值的方式来给数据成员指定初值。
- 成员初始化列表是在数据成员定义的同时赋初值,
- 但是构造函的函数体是采用先定义后赋值的方式来做。
这样就限制了,有些情况必须用成员初始化列表。
比如说
class A {
public:
int m;
A(int m1) :m(m1){}
};
class B {
public:
int x;
A a;
B(int x1, int m1) :a(m1){ x = x1; }
};
这个例子中就必须使用a(m1)
因为A的构造函数被重写了,无参构造函数不存在,如果不使用初始化表方式对a进行初始化,默认会调用A(),但事实上没有这个构造函数,因此会报错。
但如果A有无参构造函数,则可以
class A {
public:
int m;
A(int m1) :m(m1){}
A() { m = 0; }
};
class B {
public:
int x;
A a;
B(int x1, int m1) { a = A(m1); x = x1; }
};
但是在这里,相当于a被初始化了两次,浪费了时间。
拷贝构造函数
对于包含成员变量的类而言
- 默认拷贝构造函数,调用成员对象的拷贝构造函数
- 自定义拷贝构造函数,调用成员变对象的默认构造函数
void main() {
B b1;
cout << "b1.z:" << b1.z << " b1.a.x:" << b1.a.x << " b1.a.y:" << b1.a.y<<endl;
b1.inc();
cout << "b1.z:" << b1.z << " b1.a.x:" << b1.a.x << " b1.a.y:" << b1.a.y << endl;
B b2(b1);
cout << "b2.z:" << b2.z << " b2.a.x:" << b2.a.x << " b2.a.y:" << b2.a.y << endl;
}
case1
class A {
public:
int x, y;
A() {
x = y = 0;
}
void inc() {
x++;
y++;
}
};
class B {
public:
int z;
A a;
B() { z = 0; }
B(const B& b) { z = b.z; }
void inc() {
z++;
a.inc();
}
};
case2
class A {
public:
int x, y;
A() {
x = y = 0;
}
void inc() {
x++;
y++;
}
};
class B {
public:
int z;
A a;
B() { z = 0; }
B(const B& b) :a(b.a){ z = b.z; }
void inc() {
z++;
a.inc();
}
};
case3
class A {
public:
int x, y;
A() {
x = y = 0;
}
void inc() {
x++;
y++;
}
};
class B {
public:
int z;
A a;
B() { z = 0; }
// B(const B& b) :a(b.a){ z = b.z; }
void inc() {
z++;
a.inc();
}
};