构造函数
说明
explicit
- 默认构造函数一般需要加上,防止隐式转换
- 拷贝构造函数和移动构造函数一般不需要,它们就是要进行同类转换
列表初始化
- 推荐使用列表初始化,更加简洁且可以解决一些问题(如引用属性初始化)
- 无法使用时再考虑传统的初始化
const
- 靠近谁就修饰谁,使其只读,函数参数一般添加,防止修改原对象
- 构造时类有引用属性不需要添加
noexcept
- 不要向外抛出异常,可以提高性能
- 将移动构造函数、移动赋值运算符和交换函数标记为
noexcept
默认构造函数
- 无参或所有参数都有默认值的构造函数。
- 一般情况(无引用属性,无指针属性)使用
default
默认生成(无默认值),否则需要自己初始化
cpp
#include <iostream>
class Student
{
public:
explicit Student() = default;
private:
std::string name;
int age;
};
cpp
#include <iostream>
class Student
{
public:
explicit Student(int age, const std::string &name) : age(age), name(name) {}
private:
std::string name;
int age;
};
拷贝构造函数
从同类型对象创建新对象的构造函数
cpp
#include <iostream>
class Student
{
public:
// 默认构造
explicit Student() = default;
// 拷贝构造
Student(const Student &other) : name(other.name), age(other.age) {}
private:
std::string name;
int age;
};
int main()
{
Student s1;
// 两种方式都是调用拷贝构造
Student s2(s1);
Student s3 = s1;
return 0;
}
移动拷贝构造
参数是类的右值引用
cpp
#include <iostream>
class Student
{
public:
// 默认构造
explicit Student() = default;
// 移动构造(std::move为了避免string对象深拷贝效率低,不使用和age一样直接赋值也不会报错)
Student(Student &&stu) noexcept : name(std::move(stu.name)), age(stu.age) {}
private:
std::string name;
int age;
};
int main()
{
Student s1;
// 两种方式都是在调用移动构造函数来初始化对象
Student s2(std::move(s1));
Student s3 = std::move(s1);
return 0;
}
含引用属性
- 只适合用于单个对象,如配置类只需要一个
- 必须在构造函数使用列表初始化
cpp
#include <iostream>
class Student
{
public:
// 默认构造
explicit Student(std::string &name, int age) : name(name), age(age) {}
// 拷贝构造
Student(const Student &other) : name(other.name), age(other.age) {}
private:
// 必须在构造函数初始化,并且使用列表初始化
// 无论是否动态,多个对象都会共享同一个name,因此只适合单个对象的情况,如配置类
std::string &name;
int age;
};
int main()
{
std::string name = "Jerry";
Student s1(name, 18);
return 0;
}
规则
Rule of Zero
:如果不需要自定义析构函数、拷贝/移动操作,全部不定义Rule of Three
:如果需要自定义析构函数,也应该自定义拷贝构造和拷贝赋值Rule of Five
(C++11):如果需要自定义任何一个,考虑定义所有五个(析构、拷贝构造、拷贝赋值、移动构造、移动赋值)