类的特殊成员函数
默认构造函数
描述
默认构造函数是可以无实参调用的构造函数(以空参数列表定义,或为每个形参提供默认实参而定义).拥有公开默认构造函数的类型是可默认构造(DefaultConstructible)的
拷贝构造函数 & 拷贝赋值运算符
class A {
public:
A(A &a) {
std::cout << "copy constructor" << std::endl;
};
A& operator=(A &a) {
std::cout << "copy assignment operator" << std::endl;
};
A() {
std::cout << "default constructor" << std::endl;
}
};
int main() {
A a1;
A a2 = a1;
A a3 = a1;
a3 = a2;
}
输出:
default constructor
copy constructor
copy constructor
copy assignment operator
- 当创建新对象时,调用拷贝构造函数
- 当对象存在时, 譬如对象a3, 对其进行更新时, 调用拷贝赋值运算符
移动构造函数 & 移动赋值运算符(C++11)
class A {
public:
A(A &a) {
std::cout << "copy constructor" << std::endl;
};
A& operator=(A &a) {
std::cout << "copy assignment operator" << std::endl;
};
A() {
std::cout << "default constructor" << std::endl;
}
A(A &&a) {
std::cout << "move constructor" << std::endl;
}
A&& operator=(A&& a) {
std::cout << "move assignment operator" << std::endl;
}
};
int main() {
A a1;
A a2 = std::move(a1);
A a3 = a1;
a3 = std::move(a1);
}
输出:
default constructor
move constructor
copy constructor
move assignment operator
- 当创建新对象时且输入对象为右值时,调用移动构造函数
- 当对象已经存在且更新值为右值时,调用移动赋值运算符
转换构造函数
不以说明符explict声明且可以单个参数调用(c++11前)的构造函数被称为转换构造函数(converting constructor).通常的说法是转换构造函数指定了一个其实参型到其类类型的隐式转换.注意非explicit用户定义转换函数也指定一个隐式转换.
隐式声明的及用户定义的非explicit复制构造函数与移动构造函数也是转换构造函数
struct A
{
A() { } // 转换构造函数 (C++11 起)
A(int) { } // 转换构造函数
A(int, int) { } // 转换构造函数 (C++11 起)
};
struct B
{
explicit B() { }
explicit B(int) { }
explicit B(int, int) { }
};
int main()
{
A a1 = 1; // OK:复制初始化选择 A::A(int)
A a2(2); // OK:直接初始化选择 A::A(int)
A a3{4, 5}; // OK:直接列表初始化选择 A::A(int, int)
A a4 = {4, 5}; // OK:复制列表初始化选择 A::A(int, int)
A a5 = (A)1; // OK:显式转型进行 static_cast,为直接初始化
// B b1 = 1; // 错误:复制初始化不考虑 B::B(int)
B b2(2); // OK:直接初始化选择 B::B(int)
B b3{4, 5}; // OK:直接列表初始化选择 B::B(int, int)
// B b4 = {4, 5}; // 错误:复制列表初始化选择了 explicit 构造函数 B::B(int, int)
B b5 = (B)1; // OK:显式转型进行 static_cast,为直接初始化
B b6; // OK:默认初始化
B b7{}; // OK:直接列表初始化
// B b8 = {}; // 错误:复制列表初始化选择了 explicit 构造函数 B::B()
}
C++11之后添加了列表初始化, 并不局限于C++11前单个参数.
is_convertible
template<class From, class To> struct is_convertible;
Trait class that identifies whether From is implicitly convertible to To.
- 单个参数的场景可以用此来判断是否能够隐式转换
- 当多个参数的时候, 就无法使用了
std::cout << std::is_convertible<int, A>::value << std::endl;
继承构造函数
之前
可以让派生类直接使用基类的构造函数,而无须自已再写构造函数,尤其是在基类有很多构造函数的情况下,可以极大地简化派s生类构造函数的编写
class Test {
public:
Test(int i) {
std::cout << "Test(i)" << i << std::endl;
}
private:
int x_;
};
class TestA : public Test {
public:
TestA(int i) : Test(i) {
std::cout << "TestA()" << std::endl;
}
};
现在
class TestA : public Test {
using Test::Test;
};
委派构造函数
允许在同一个类中一个构造函数可以调用另外一个构造函数, 从而可以在初始化时简化变量初始化.
没有使用构造函数的场景
class TestB {
public:
TestB(int x):x_(x){};
TestB(int x, int y):x_(x), y_(y) {};
TestB(int x, int y, int z):x_(x), y_(y), z_(z) {
std::cout << "x:" << x_ << " y:" << y_ << " z:" << z_ << std::endl;
};
private:
int x_;
int y_;
int z_;
};
int main() {
TestB b(1,2,3);
}
委派构造函数使用场景
class TestB {
public:
TestB(int z):z_(z){};
TestB(int x, int y):x_(x), y_(y) {};
TestB(int x, int y, int z):TestB(x, y) {
std::cout << "x:" << x_ << " y:" << y_ << " z:" << z_ << std::endl;
};
private:
int x_;
int y_;
int z_;
};
- 有且只能调用一个其它构造函数
- 不能在含有委派构造函数的情况下初始化其它变量,譬如
TestB(int x, int y, int z):TestB(x, y),z_(z) {
std::cout << "x:" << x_ << " y:" << y_ << " z:" << z_ << std::endl;
};