构造函数
构造函数 构造函数是: 创建对象时自动调用的函数 。 1.默认构造函数 没有参数的构造函数 叫默认构造函数 2.带参构造函数 带参构造函数就是创建对象时传入初始值 3. 初始化列表 必须使用初始化列表的情况 const 成员变量 不能写成: 因为 const 变量出生以后就不能改了,所以必须在出生时...
构造函数
构造函数是:创建对象时自动调用的函数。
1.默认构造函数
没有参数的构造函数叫默认构造函数
class Student {
private:
string name;
int age;
public:
Student() {
name = "unknown";
age = 0;
}
void show() const {
cout << name << " " << age << endl;
}
};
2.带参构造函数
带参构造函数就是创建对象时传入初始值
class Student {
private:
string name;
int age;
public:
Student(string n, int a) {
name = n;
age = a;
}
};
3. 初始化列表
class Student {
private:
string name;
int age;
public:
Student(string n, int a) : name(n), age(a) {
}
};
必须使用初始化列表的情况
const 成员变量
class Student {
private:
const int id;
public:
Student(int x) : id(x) {
}
};
不能写成:
class Student {
private:
const int id;
public:
Student(int x) {
id = x; // 错误
}
};
因为 const 变量出生以后就不能改了,所以必须在出生时初始化。
引用成员变量
class Test {
private:
int& ref;
public:
Test(int& x) : ref(x) {
}
};
引用也必须一开始就绑定。
不能写:
Test(int& x) {
ref = x; // 错误理解
}
这里不是让 ref 引用 x,而是给 ref 当前绑定的对象赋值。但问题是 ref 还没绑定任何对象。
成员对象没有默认构造函数
比如有一个类:
class Date {
private:
int year;
int month;
int day;
public:
Date(int y, int m, int d) : year(y), month(m), day(d) {}
};
现在另一个类里面有 Date 成员:
class Student {
private:
string name;
Date birthday;
public:
Student(string n, int y, int m, int d)
: name(n), birthday(y, m, d) {
}
};
意思是调用 Date 的构造函数来初始化 birthday。
不能这样写:
class Student {
private:
string name;
Date birthday;
public:
Student(string n, int y, int m, int d) {
name = n;
birthday = Date(y, m, d); // 通常会出错
}
};
因为进入构造函数体之前,birthday 已经必须先被构造好。但 Date 没有无参构造函数,所以构造不出来。
初始化顺序
成员变量的初始化顺序不是看初始化列表写的顺序,而是看成员变量声明顺序。
比如:
class Test {
private:
int a;
int b;
public:
Test() : b(2), a(1) {
}
};
虽然初始化列表写的是:
b(2), a(1)
但实际还是:
a 先初始化
b 后初始化
因为声明顺序是:
int a;
int b;
所以建议你永远按照声明顺序写初始化列表:
class Test {
private:
int a;
int b;
public:
Test() : a(1), b(2) {
}
};
4.拷贝构造函数
拷贝构造函数可以理解成:
用一个已经存在的对象,创建一个新的对象。
它是“出生时复制”,不是“出生后赋值”。
基本写法:
class Student {
private:
int age;
public:
Student(int age = 0) : age(age) {}
// 拷贝构造函数
Student(const Student& other) : age(other.age) {
cout << "调用拷贝构造函数" << endl;
}
};
标准格式是:
类名(const 类名& other)
4.1 为什么要用 &
如果写成这样:
Student(Student other)
这是错误的。
因为调用拷贝构造函数时,要先把实参复制给形参 other。
但是复制 other 又需要调用拷贝构造函数。
于是就变成:
调用拷贝构造
为了传参,又调用拷贝构造
为了传参,又调用拷贝构造
...
无限递归。
所以必须写引用:
Student(Student& other)
这样传参时不会再复制一份。
4.2 为什么要用 const?
写成:
Student(const Student& other)
表示:
我只是拿
other来复制数据,不会修改other。
而且有了 const,它可以接收普通对象,也可以接收 const 对象。
const Student s1(18);
Student s2(s1); // 需要 const Student&
4.3默认拷贝构造函数
即使你不写拷贝构造函数,编译器也会自动生成一个。
比如:
class Point {
private:
int x;
int y;
public:
Point(int x = 0, int y = 0) : x(x), y(y) {}
};
你没写:
Point(const Point& other)
但仍然可以:
Point p1(1, 2);
Point p2 = p1;
编译器默认会帮你做:
p2.x = p1.x;
p2.y = p1.y;
这种叫浅拷贝,也就是成员一个一个复制。