发布时间:2024-06-22 13:01
构造对象时调用的函数,名称与类名相同,没有返回值,可以包含多个版本(重载)
#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");
}
通常要求我们写代码的时候,初始化列表的顺序和声明顺序相同。
不然让读代码的人,有疑惑。
使用初始化列表覆盖类内成员初始化
#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);
}
#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; // // 编译器自动合成拷贝赋值函数
}