C++类 默认函数

201 阅读3分钟

C++类特殊函数

类中的几个特殊函数

 A() //构造函数 
 ~A() //析构函数 
 A(const A&) //复制构造函数 
 A& operator = (const A &) //复制赋值运算符
 A(A&&) //移动构造函数
 A& operator=(A&& a)//移动赋值函数
 //以上为默认函数
explicit operator bool() //向外转换
     

通过 = default= delete来控制默认函数

具体的隐式声明情况见

默认构造函数析构函数 复制构造函数复制赋值 移动构造函数 (C++11) 移动赋值 (C++11) 转换构造函数explicit 说明符

示例

using namespace std;
#include<iostream>;
#include<initializer_list>
#include<memory>
struct T
{
	int n;
	T() { cout << "T()" << endl; }
	T(int n) : n(n) { cout << "T(int n)" << endl; }
};

构造函数与析构函数

class A
{
	T x;
	int y;
	int* z;
	shared_ptr<int> u;
	unique_ptr<int> v;
public:
	//构造函数
	A() :x(0),
		y(0),
		z(new int(0)),
		u(new int(1)),
		v(new int(2))
	{
		cout << "A()" << endl;
	}
	//析构函数
	~A() { cout << "~A()" << endl; delete z; }
	//转换函数
	A(const int& x, const int& y) : x(x),
		y(0),
		z(new int(0)),
		u(new int(1)),
		v(new int(2))
	{
		cout << "A(const int& x, const int& y)" << endl;
	}

调用方式

//默认构造函数
	A();
	//转换构造函数//自定义构造函数
	A a(1, 2);
	A a2{ 1,2 };									   // modern C++
	A a3 = { 1,2 }; 			 	          //explicit 声明后不能使用
	A a4 = A(1, 2);				          //explicit 声明后不能使用
	A&& a9 = A(2, 3);//右值		  //explicit 声明后不能使用
	A&& a10(A(2, 3));//右值
	A&& a11{ 2,3 };//右值
	A&& a12(A{ 2,3 });//右值

复制构造函数

//复制构造函数
//形参 T&、const T&、volatile T& 或 const volatile T&
A(const A& a) :
	x(a.x),
	y(a.y),
	z(new int{ *a.z }),//deep copy
	u(a.u),//share
	v(new int{ *a.v })//deep copy
{
	cout << "A(const A& a)" << endl;
}

浅拷贝(默认)

复制所有成员值

深拷贝(自定义)

对于指针成员对象,开辟新的内存并拷贝

调用方式

  • 初始化:T a = b; 或 T a(b);,其中 b 的类型是 T
  • 函数实参传递:f(a);,其中 a 的类型是 Tf 是 void f(T t);
  • 函数返回:在像 T f() 这样的函数内部的 return a;,其中 a 的类型是 T 且它没有移动构造函数
	//复制构造函数
	A a5(a);
	A a6 = a;	//explicit 声明后不能使用

复制赋值运算符


	//复制赋值运算符
	//形参T、T&、const T&、volatile T& 或 const volatile T&
	A& operator=(const A& a)
	{
		cout << "A& operator=(const A& a)" << endl;
		x = a.x;
		y = a.y;
		*z = *a.z;//deep copy
		u = a.u;//share
		v = make_unique<int>(*a.v);//deep copy

		return *this;
	}

调用方法

	//复制赋值运算符
	a5 = a2;

移动构造函数

//移动构造函数//std::move
	//形参 T&&、const T&&、volatile T&& 或 const volatile T&&
	A(A&& a) :
		//类类型成员隐式移动
		x(a.x),
		//非类类型成员的隐式移动
		y(a.y),
		///z(a.z) //未删除原指针 多次释放内存
		//类类型成员显式移动
		//x(std::move(a.x)),
		//y(std::move(a.y)),
		//非类类型成员的显式移动
		//y(std::exchange(a.y, 0))
		///指针的显式移动
		z(std::exchange(a.z, nullptr)),
		u(std::exchange(a.u, nullptr)),
		v(std::exchange(a.v, nullptr))
	
	{
		cout << "A(const A&& a)" << endl;
	}

std::exchange

template< class T, class U = T >
T exchange(T& obj, U&& new_value);//(C++14 起)//(C++20 前)
	
template< class T, class U = T >
constexpr T exchange(T& obj, U&& new_value);//(C++20 起)//(C++23 前)
	
template< class T, class U = T >
constexpr T exchange(T& obj, U&& new_value) noexcept;//(C++23 起)

注意指针移动,避免重复释放资源

智能指针同样采用std::exchange

调用

	//移动构造函数
	A a7(std::move(a));
	A a8 = std::move(a2);

区别于右值引用

	//右值引用
	A&& a13 = std::move(a3);
	A&& a14 = (A&&)a4;

左值引用和右值引用

  • 左值引用:引用一个对象;
  • 右值引用:就是必须绑定到右值的引用,C++11中右值引用可以实现“移动语义”,通过 && 获得右值引用。
int x = 6; // x是左值,6是右值
int &y = x; // 左值引用,y引用x

int &z1 = x * 6; // 错误,x*6是一个右值
const int &z2 =  x * 6; // 正确,可以将一个const引用绑定到一个右值

int &&z3 = x * 6; // 正确,右值引用
int &&z4 = x; // 错误,x是一个左值

右值引用和相关的移动语义是C++11标准中引入的最强大的特性之一,通过std::move()可以避免无谓的复制,提高程序性能。

移动赋值运算符

解释

\1) 移动赋值运算符的典型声明。

\2) 强制编译器生成移动赋值运算符。

\3) 避免隐式移动赋值。

每当重载决议选择移动赋值运算符时,它都会被调用,例如当对象出现在赋值表达式左侧,而其右侧是同类型或可隐式转换的类型的右值时

典型的移动赋值运算符“窃取”实参曾保有的资源(例如指向动态分配对象的指针,文件描述符,TCP socket,输入输出流,运行的线程,等等),而非复制它们,并使得实参遗留在某个合法但不确定的状态

	//移动赋值函数
	//形参 T&&、const T&&、volatile T&& 或 const volatile T&&
	A& operator=(A&& a)
	{
		cout << "A& operator=(const A&& a)" << endl;
		x = a.x;
		y = a.y;
		z = std::exchange(a.z, nullptr);
		u = std::exchange(a.u, nullptr);
		v = std::exchange(a.v, nullptr);
		return *this;
	}

调用

	//移动赋值函数
	a7 = std::move(a5);
	a7 = { 2,3 };//右值//先转换构造一类
	a7 = A(2, 3);//右值