文章目录
- 构造函数的概念
-
- 构造函数
- 初始化列表
- 特殊的构造函数
-
- 缺省构造函数
- 单一参数构造函数
- 拷贝构造函数
- 移动构造函数
- 拷贝赋值与移动赋值函数(operator =)
构造函数的概念
文章图片
构造函数 构造对象时调用的函数,名称与类名相同,没有返回值,可以包含多个版本(重载)
#include
#include
using namespace std;
class Str
{
public:
// 构造函数
Str()
{
cout << "Constructor is called." << endl;
}// 构造函数的重载
Str(int input)
{
x = input;
cout << x << endl;
}
private:
int x;
};
int main()
{
Str m;
// 调用构造函数Str m1(3);
}
代理构造函数:
代理构造函数先执行,然后继续执行原始构造函数
#include
#include
using namespace std;
class Str
{
public:
// 代理构造函数,让下面的构造函数代理
Str() : Str(3)
{
cout << "here1" << endl;
// 后执行
}// 构造函数的重载
Str(int input)
{
cout << "here2" << endl;
// 先执行
x = input;
}void fun()
{
cout << x << endl;
}
private:
int x;
};
int main()
{
Str m;
// 调用构造函数
m.fun();
}
初始化列表 没有使用初始化列表:
#include
#include
using namespace std;
class Str
{
public:
Str(const std::string& val)
{
cout << "Pre-assignment: " << x << endl;
// 空字符, 缺省初始化了
x = val;
// 这里是赋值,不是定义
cout << "Post-assignment: " << x << endl;
// "abc"
}private:
std::string x;
};
int main()
{
Str m("abc");
}
使用了初始化列表:
#include
#include
using namespace std;
class Str
{
public:
Str(const std::string& val) : x(val), y(1) // 初始化列表
{
cout << "Pre-assignment: " << x << endl;
// 也是“abc”
cout << "Post-assignment: " << x << endl;
// "abc"
cout << "Pre-assignment: " << y << endl;
// 1
cout << "Post-assignment: " << y << endl;
// 1
}private:
std::string x;
int y;
};
int main()
{
Str m("abc");
}
通过引用进行初始化:
#include
#include
using namespace std;
class Str
{
public:
Str(const std::string& val, int& p_i)
: x(val)
, y(1)
, ref(p_i)
{
cout << "Pre-assignment: " << x << endl;
// 也是“abc”
cout << "Post-assignment: " << x << endl;
// "abc"
cout << "Pre-assignment: " << y << endl;
// 1
cout << "Post-assignment: " << y << endl;
// 1
ref = 100;
}private:
std::string x;
int y;
int& ref;
};
int main()
{
int val;
Str m("abc", val);
cout << val << endl;
// 100 通过引用进行初始化
}
注意,元素的初始化顺序与其声明相关,与初始化列表顺序无关
c++构造和销毁对象是按照一定顺序的,比如先构造了A,再构造了B。那么销毁的时候是先销毁B,再销毁A。
文章图片
#include
#include
using namespace std;
class Str
{
public:
// 不管初始化列表顺序,而是按照声明顺序,c++不需要关注对象调用了哪个构造函数,而是关注声明顺序再决定销毁顺序。
Str()
: x("")
, y(1)
{
}Str(const std::string& val)
: y(x.size()) // 与初始化列表顺序无关,与声明顺序有关
, x(val)
{
cout << x << endl;
cout << y << endl;
}private:
// 声明的顺序是x在y前面,在内存先开x的内存,再开y
std::string x;
size_t y;
};
int main()
{
Str m("abc");
}
通常要求我们写代码的时候,初始化列表的顺序和声明顺序相同。
不然让读代码的人,有疑惑。
【C++|C++ 基础与深度分析 Chapter11 类与面向对象编程(构造函数(缺省、单一、拷贝、移动、赋值))】使用初始化列表覆盖类内成员初始化
#include
#include
using namespace std;
class Str
{
public:Str()
: x(50)
, y(100)
{
cout << x << endl;
// 50
cout << y << endl;
// 100 会覆盖类内成员初始化
}private:
size_t x = 3;
size_t y = 4;
};
int main()
{
Str m;
}
特殊的构造函数 缺省构造函数 不需要提供实际参数就可以调用的构造函数
文章图片
文章图片
如果类中没有任何构造函数,在允许的条件下,编译器会合成一个缺省的构造函数。
文章图片
如果提供了构造函数,编译器就不会自动合成默认构造函数。
文章图片
调用缺省构造函数时,避免most vexing parse
文章图片
用default来定义缺省构造函数
文章图片
单一参数构造函数
文章图片
缺省构造函数是包含0个参数的构造函数。
单一参数的构造函数,就是包含1个参数的构造函数。
#include
#include
using namespace std;
struct Str
{// explicit必须显式的初始化,不能隐式初始化
explicit Str(int x)
// Str(int x)
: val(x)
{}private:
int val;
};
void fun(Str m)
{}int main()
{
Str m(3);
// 下面显式初始化式不行的
// Str m1 = 3;
// 也可以这么调用构造函数,把3通过单一构造函数转化成Str m1类型
// fun(3);
// 3转化成Str类型
}
拷贝构造函数
文章图片
文章图片
文章图片
如果未显示提供,那么编译器会合成一个,合成的版本会依次对每个数据成员调用拷贝构造。
移动构造函数
文章图片
接收一个当前类右值引用对象的构造函数。进一步提升性能。
文章图片
int main()
{
std::string ori("abc");
std::string newStr = ori;
// string 的拷贝构造
cout << newStr << endl;
cout << ori << endl;
}
int main()
{
std::string ori("abc");
std::string newStr = std::move(ori);
// 构造了一个右值,将亡值
cout << newStr << endl;
// abc
cout << ori << endl;
// 空
}
#include
#include
#include
using namespace std;
struct Str
{
Str() = default;
Str(Str&& x)
: val(x.val)
, a(std::move(x.a))
{
}void fun()
{
cout << val << ' ' << a << endl;
}int val = 3;
std::string a = "abc";
};
int main()
{
Str m;
m.fun();
// 3 abc
Str m1 = std::move(m);
m.fun();
// 3 "" , 字符串move,移动构造函数
m1.fun();
// 3 abc
}
移动构造函数通常不会抛出异常,因为异常一般出现在内存的分配时,但是移动构造不存在内存的分配,只是偷资源,拷贝可能会出现异常。
#include
#include
#include
using namespace std;
struct Str2
{
Str2() = default;
// Str2拷贝构造
Str2(const Str2&)
{
cout << "Str2 copy constuctor is called" << endl;
}/*
// Str2移动构造(有移动调移动,没有移动调拷贝)
Str2(Str2&&)
{
cout << "Str2 move constuctor is called" << endl;
}
*/};
struct Str
{
Str() = default;
Str(const Str&) = default;
Str(Str&&) = default;
// 缺省移动构造函数// 对每一个类型调用默认的移动构造
int val = 3;
// 内建类型去赋值
std::string a = "abc";
// strig类型去移动
Str2 m_str2;
// 这个数据成员只有拷贝构造函数,没有移动构造函数,那么Str的移动构造函数
// 在处理Str2时,会调用Str2的拷贝构造函数。
// 换句话说,Str2有移动,调移动,没移动构造,调拷贝构造
};
int main()
{
Str m;
Str m2 = std::move(m);
}
右值引用对象用作表示式时是左值。
文章图片
拷贝赋值与移动赋值函数(operator =)
文章图片
operator = ,运算符重载。
#include
#include
#include
using namespace std;
struct Str
{
Str() = default;
Str(const Str&) = default;
// 拷贝构造函数
Str(Str&& x) noexcept = default;
// 移动构造函数// 拷贝赋值函数,赋值函数不能使用初始化列表,m2已经完成初始化过了,这里是赋值
// 返回当前类型的引用,return *this
Str& operator= (const Str& x)
{
cout << "copy assignment is called" << endl;
val = x.val;
a = x.a;
return *this;
}// 移动赋值函数
Str& operator= (Str&& x)
{
if (&x == this)
{
cout << "dummy assignment is called" << endl;
return *this;
// 处理给自身赋值的情况
}
cout << "move assignment is called" << endl;
val = std::move(x.val);
a = std::move(x.a);
return *this;
}int val = 3;
std::string a = "abc";
};
int main()
{
Str m;
Str m2;
// m2 = m;
// 这不再是构造,而是一个赋值的过程
// m2 = std::move(m);
// 调用移动赋值函数
m = std::move(m);
}
在一些情况下,编译器会自动合成。
#include
#include
#include
using namespace std;
struct Str
{int val = 3;
std::string a = "abc";
int* ptr;
};
int main()
{
Str m;
Str m2;
Str m3;
m = std::move(m2);
// 编译器自动合成移动赋值函数
m = m3;
// // 编译器自动合成拷贝赋值函数
}
推荐阅读
- C++|很值得学习的Linux C++线程池框架
- C++|初识C++模板(函数、类模板)
- 性能优化|Android 项目架构系列之代码的混淆
- mysql|SQL优化万能公式(5 大步骤 + 10 个案例)
- #|【JAVA后端开发】Part1--瑞吉外卖项目
- Java初级学习笔记|Java语法之多态、抽象类、引用型的数据类型转换、Object类的使用
- 比赛经验|2022年6月第十三届蓝桥杯软件类国赛(决赛)C组C语言/C++真题及答案 with 感想
- Qt入门|Qt QScrollArea
- Qt入门|Qt QProgressBar详解