构造函数
- 类的一种特殊的成员函数
- 它会在每次创建类的新对象时执行
- 名字与类名相同,没有任何返回值
作用
- 用于为某些成员变量设置初始值
默认构造
没有参数的构造
类存在有参构造函数时,编译器不会自动添加默认构造函数,这时想要编译器添加默认构造函数,解决:People() = default;
class People{ private: string name; int age; public: // 默认构造 People() = default; //让编译器自动添加默认构造函数 // People() = delete; //该构造函数不能使用 People(string newName) { name = newName; age = 18; } People(int newAge) { name = "People"; age = newAge; } People(string newName, int newAge) { name = newName; age = newAge; } };
class People{
private:
string name;
int age;
public:
// 默认构造
People();
};
有参构造
- 有参数的构造
#include
#include
using std::cout;
using std::endl;
using std::string;
class People{
private:
string name;
int age;
public:
// 默认构造
People() {
name = "People";
age = 18;
}
// 显性 explicit
// 隐式 implicit
explicit People(string newName) {
name = newName;
age = 18;
}
People(int newAge) {
name = "People";
age = newAge;
}
People(string newName, int newAge) {
name = newName;
age = newAge;
}
};
int main() {
return 0;
}
注意:
- 不能存在参数类型顺序相同的构造函数
- 即People(int a) 与 People(int b) 有且只能存在一个;
- 只有一个参数时,编译器默认进行隐式转换
拷贝构造函数
- 拷贝构造函数不写的话,编译器自动生成一个浅拷贝的拷贝构造函数
class People{
private:
string name;
int age;
public:
// 默认构造
People() {
name = "People";
age = 18;
}
// 有参构造函数
People(string newName) {
name = newName;
age = 18;
}
People(int newAge) {
name = "People";
age = newAge;
}
People(string newName, int newAge) {
name = newName;
age = newAge;
}
// 拷贝构造
People(const People& p) {
name = p.name;
age = p.age;
}
};
为啥传const类型的引用?
- 引用的C++底层原理为const类型的指针, 不能先定义后赋值;
- 值传递会造成拷贝构造函数死循环。
参数初始化列表
意义与作用
- 引用参数初始化列表, 没有进入函数体内部
- 直接调用name的构造有参构造函数
- 进入函数体内部前,初始化完成。
- 写在函数体内部时
- 第一步调用name的默认构造函数初始化;
- 第二步调用string operator= 的操作符重载函数;
代码1测试
#include
using namespace std;
class Point{
private:
float x, y;
public:
~Point() {
cout << "Point析构" << endl;
}
Point() : x(0), y(0) {
cout << "Point默认构造" << endl;
}
Point(float xy) : x(xy), y(x) {
cout << "Point一个参数的构造" << endl;
}
Point(float x, float y) : x(x), y(y) {
cout << "Point两个参数的构造" << endl;
}
Point(const Point& p) : x(p.x), y(p.y) {
cout << "Point拷贝构造" << endl;
}
Point& operator=(const Point& p) {
cout << "赋值运算符重载";
if (this != &p) {
this->x = p.x;
this->y = p.y;
cout << ", ..., 赋值运算符重载成功!" << endl;
}
return *this;
}
};
class Line{
private:
Point p1, p2;
public:
~Line() {
cout << "Line析构" << endl;
}
Line() : p1{0, 0}, p2{0, 0} {
cout << "Line的默认构造" << endl;
}
Line(const Point& p) : p1(p), p2(p1) {
cout << "Line的一个参数的构造" << endl;
}
// 发生两次Point的拷贝构造,一次Line的拷贝构造
// Line(const Point& p1, const Point& p2) : p1(p1), p2(p2) {
// cout << "Line的两个参数的构造" << endl;
// }
Line(const Point& p1, const Point& p2) {
cout << "Line的两个参数的构造开始" << endl;
this->p1 = p1;
this->p2 = p2;
cout << "Line的两个参数的构造完成" << endl;
}
Line(const Line& l) : p1(l.p1), p2(l.p2) {
cout << "Line拷贝构造" << endl;
}
};
int main() {
Point p1(1.1f, 2.3f), p2(1.2f, 2.3f);
cout << "&&&&&&&&&&&&&&&&&&" << endl;
Line l(p1, p2);
cout << "&&&&&&&&&&&&&&&&&&" << endl;
Line l1;
return 0;
}
代码2测试
#include
using namespace std;
class Point{
private:
float x, y;
public:
~Point() {
cout << "Point析构" << endl;
}
Point() : x(0), y(0) {
cout << "Point默认构造" << endl;
}
Point(float xy) : x(xy), y(x) {
cout << "Point一个参数的构造" << endl;
}
Point(float x, float y) : x(x), y(y) {
cout << "Point两个参数的构造" << endl;
}
Point(const Point& p) : x(p.x), y(p.y) {
cout << "Point拷贝构造" << endl;
}
Point& operator=(const Point& p) {
cout << "赋值运算符重载";
if (this != &p) {
this->x = p.x;
this->y = p.y;
cout << ", ..., 赋值运算符重载成功!" << endl;
}
return *this;
}
};
class Line{
private:
Point p1, p2;
public:
~Line() {
cout << "Line析构" << endl;
}
Line() : p1{0, 0}, p2{0, 0} {
cout << "Line的默认构造" << endl;
}
Line(const Point& p) : p1(p), p2(p1) {
cout << "Line的一个参数的构造" << endl;
}
// 发生两次Point的拷贝构造,一次Line的拷贝构造
Line(const Point& p1, const Point& p2) : p1(p1), p2(p2) {
cout << "Line的两个参数的构造" << endl;
}
// Line(const Point& p1, const Point& p2) {
// cout << "Line的两个参数的构造开始" << endl;
// this->p1 = p1;
// this->p2 = p2;
// cout << "Line的两个参数的构造完成" << endl;
// }
Line(const Line& l) : p1(l.p1), p2(l.p2) {
cout << "Line拷贝构造" << endl;
}
};
int main() {
Point p1(1.1f, 2.3f), p2(1.2f, 2.3f);
cout << "&&&&&&&&&&&&&&&&&&" << endl;
Line l(p1, p2);
cout << "&&&&&&&&&&&&&&&&&&" << endl;
Line l1;
return 0;
}
测试结果
1、代码1测试结果
Point两个参数的构造
Point两个参数的构造
&&&&&&&&&&&&&&&&&&
Point默认构造
Point默认构造
Line的两个参数的构造开始
赋值运算符重载, ..., 赋值运算符重载成功!
赋值运算符重载, ..., 赋值运算符重载成功!
Line的两个参数的构造完成
&&&&&&&&&&&&&&&&&&
Point两个参数的构造
Point两个参数的构造
Line的默认构造
Line析构
Point析构
Point析构
Line析构
Point析构
Point析构
Point析构
Point析构
2、代码二测试结果
Point两个参数的构造
Point两个参数的构造
&&&&&&&&&&&&&&&&&&
Point拷贝构造
Point拷贝构造
Line的两个参数的构造
&&&&&&&&&&&&&&&&&&
Point两个参数的构造
Point两个参数的构造
Line的默认构造
Line析构
Point析构
Point析构
Line析构
Point析构
Point析构
Point析构
Point析构
分析
- 可以看出来,结果二比结果一少了赋值运操作
#include
#include
using namespace std;
class People{
friend int main();
//用友员函数不用成员函数的原因:
/*
1、cout是ostream类的对象
2、这个函数的第一个对象不是当前类的对象,而是ostream类的
*/
friend ostream& operator<<(ostream& os, const People&);
private:
string name;
int age;
public:
// 默认构造
People() : name("People"), age(18) {
// 引用参数初始化列表, 没有进入函数体内部
// 直接调用name的构造有参构造函数
// 进入函数体内部前,初始化完成。
// 写在函数体内部:
name = "People";
// 第一步调用name的默认构造函数,
// 第二部调用string operator= 的操作符重载函数
// age = 18;
}
// 有参构造
People(string s) : name(s), age(18) {
cout << "string constructor" << endl;
}
// implicit 隐式转换
// explicit 显式构造
// 编译器默认隐式转换,即为implicit
explicit People (int age) : name("People"), age(age) {
cout << "int constructor" << endl;
}
// 拷贝构造函数
People(const People& p) : name(p.name), age(p.age) {
// 引用的C++底层原理为const类型的指针, 不能先定义后赋值;
cout << "copy constructor" << endl;
// 值传递会造成拷贝构造函数死循环
}
~People() {
cout << "destructor" << endl;
}
// 赋值运算符重载
People& operator= (const People& p) {
name = p.name;
age = p.age;
cout << "call operator=" << endl;
return *this;
}
// 法一
// 使用方法:p << cout;
// 原因:当前函数的第一个参数是该类的对象
// ostream& operator<< (ostream& os) {
// os << "name = " << name << ", age = " << age << endl;
// return os;
// }
// 法二设置为友员函数
};
ostream& operator<< (ostream& os, const People& p) {
os << "name = " << p.name << ", age = " << p.age << endl;
return os;
}
int main() {
People p("Axieyun");
cout << "name = " << p.name << ", age = " << p.age << endl;
People p1(p);
cout << p;
cout << "p.arrces = " << &p << ", p1.arrces = " << &p1 << endl;
// 赋值构造
People p2 = People(18); // 隐式转换为People对象,再拷贝构造给p2;
p2 = p;
// p << cout;
cout << p;
return 0;
}
析构
- 默认构造函数前加~
class People{
private:
string name;
int age;
public:
~People() {
cout << "destructor" << endl;
// delete操作
};
};
35法则
需要析构函数的类也需要拷贝构造函数和拷贝赋值函数
需要拷贝操作的类也需要赋值操作,反之亦然
析构函数不能删除
如果一个类有删除的或不可访问的析构函数,那么其默认和拷贝构造函数会被定义为删除的
如果一个类有const或引用成员,则不能使用合成的拷贝赋值操作,即
不能进行以下操作
int b = 1; int& a; a = b;
应该这样写
int b = 1; int& a = b;
const类型同理。