首页 > 编程语言 >C++拷贝构造函数、运算符重载函数、赋值运算符重载函数、前置++和后置++重载等的介绍

C++拷贝构造函数、运算符重载函数、赋值运算符重载函数、前置++和后置++重载等的介绍

时间:2024-06-22 18:03:24浏览次数:3  
标签:Date int ._ month 运算符 ++ year 重载 day

文章目录


前言

C++拷贝构造函数、运算符重载函数、赋值运算符重载函数、前置++和后置++重载等的介绍


一、拷贝构造函数

在实际运用中,我们有时候需要操作一个实例化好的类,但是又不想改变这个类,这时候,我们就需要拷贝构造函数。

1. 概念

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

2. 特征

拷贝构造函数也是特殊的成员函数,其特征如下:

  1. 拷贝构造函数构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用

拷贝构造函数是构造函数的一个重载形式,所以拷贝构造函数的函数名与类名相同,参数与构造函数不同,无返回值。

如下图:

  • 采用传值调用,会引发无穷递归,编译器强制检查报错。
    在这里插入图片描述

  • 应该采用类类型对象的引用,如下:

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1945, int month = 8, int day = 15)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2024, 6, 22);

	Date d2(d1);


	return 0;
}

在这里插入图片描述

3. 编译器生成默认拷贝构造函数

若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝

简单来说就是,不显示定义,编译器默认生成一个构造函数

  • 对于内置类型会进行值拷贝(浅拷贝)。
  • 对于自定义类型会调用它的拷贝构造函数。
#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1945, int month = 8, int day = 15)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2023, 9, 1);

	Date d2(d1);


	return 0;
}

在这里插入图片描述

编译器默认生成的拷贝构造函数是值拷贝(浅拷贝),但是有些情况下我们需要深拷贝。比如:
对一个栈,我们不能直接浅拷贝,程序会崩溃。原因如下:

浅拷贝会将一个栈动态申请的空间的地址拷贝个另一个栈的指针,所以两个栈对象的指针指向同一块空间,析构函数会对同一块空间释放两次,程序会崩溃。

#include <iostream>
using namespace std;

class Stack
{
public:
	Stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (nullptr == _a)
		{
			perror("Stack malloc");
			return;
		}

		_top = 0;
		_capacity = capacity;
	}

	void Push(int x)
	{
		// 增容
		_a[_top] = x;
		_top++;

	}

	~Stack()
	{
		if (_top)
		{
			free(_a);
			_a = nullptr;
			
			_top = 0;
			_capacity = 0;
		}
	}
private:
	int* _a;
	int _top;
	int _capacity;
};

int main()
{
	Stack st1(10);
	st1.Push(1);
	st1.Push(2);
	st1.Push(3);
	st1.Push(4);


	Stack st2(st1);

	return 0;
}

在这里插入图片描述


在这里插入图片描述

  • 所以,这种Stack的类对象,我们应该自己显示定义拷贝构造函数。
#include <iostream>
using namespace std;

class Stack
{
public:
	Stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int) * capacity);
		if (nullptr == _a)
		{
			perror("Stack malloc");
			return;
		}

		_top = 0;
		_capacity = capacity;
	}

	Stack(const Stack& st)
	{
		_a = (int*)malloc(sizeof(int) * st._capacity);
		if (nullptr == _a)
		{
			perror("Stack malloc");
			return;
		}

		memcpy(_a, st._a, sizeof(int) * st._top);

		_top = st._top;
		_capacity = st._capacity;
	}

	void Push(int x)
	{
		// 增容
		_a[_top] = x;
		_top++;

	}

	~Stack()
	{
		if (_top)
		{
			free(_a);
			_a = nullptr;
			
			_top = 0;
			_capacity = 0;
		}
	}
private:
	int* _a;
	int _top;
	int _capacity;
};

int main()
{
	Stack st1(10);
	st1.Push(1);
	st1.Push(2);
	st1.Push(3);
	st1.Push(4);


	Stack st2(st1);

	return 0;
}

在这里插入图片描述

注意:类中如果没有涉及资源申请时拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝

4. 拷贝构造函数典型使用场景

  • 使用已存在对象创建新对象
  • 函数参数类型为类类型对象
  • 函数返回值类型为类类型对象

二、运算符重载函数

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。函数名字为:关键字operator后面接需要重载的运算符符号
函数原型:返回值类型 operator操作符(参数列表)

注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  • .*、 ::、 sizeof 、?: . 注意以上5个运算符不能重载**
#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1945, int month = 8, int day = 15)
	{
		_year = year;
		_month = month;
		_day = day;
	}


	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
//private:
	int _year;
	int _month;
	int _day;
};


bool operator<(const Date& x1, const Date& x2)
{
	if (x1._year < x2._year)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month < x2._month)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month == x2._month && x1._day < x2._day)
	{
		return true;
	}
	else
	{
		return false;
	}
}


bool operator>(const Date& x1, const Date& x2)
{
	if (x1._year > x2._year)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month > x2._month)
	{
		return true;
	}
	else if (x1._year == x2._year && x1._month == x2._month && x1._day > x2._day)
	{
		return true;
	}
	else
	{
		return false;
	}
}

int main()
{
	Date d1(1945, 8, 15);
	Date d2(1949, 10, 1);

	cout << operator<(d1, d2) << endl;
	cout << (d1 < d2) << endl;

	cout << operator>(d1, d2) << endl;
	cout << (d1 > d2) << endl;



	return 0;
}

在这里插入图片描述

使用运算符重载时,调用运算符重载函数,也可以直接使用运算符比较。

	cout << operator<(d1, d2) << endl;
	cout << (d1 < d2) << endl;

	cout << operator>(d1, d2) << endl;
	cout << (d1 > d2) << endl;
  • 一般建议直接使用运算符比较。

上述运算符重载到了全局中,运算符重载也可以重载到类中。

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1945, int month = 8, int day = 15)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	bool operator<(const Date& x2)
	{
		if (_year < x2._year)
		{
			return true;
		}
		else if (_year == x2._year && _month < x2._month)
		{
			return true;
		}
		else if (_year == x2._year && _month == x2._month && _day < x2._day)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(1945, 8, 15);
	Date d2(1949, 10, 1);

	cout << d1.operator<(d2) << endl;
	cout << (d1 < d2) << endl;

	return 0;
}

在这里插入图片描述

三、赋值运算符重载函数

1. 赋值运算符重载格式

  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义
#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1945, int month = 8, int day = 15)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}

		return *this;
	}

	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1(1945, 8, 15);
	Date d2(1949, 10, 1);

	//d2.operator=(d1);

	d2 = d1;

	return 0;
}

在这里插入图片描述

赋值运算符可以选择调用赋值运算符重载函数,也可以直接使用赋值运算符,一般建议直接使用赋值运算符。

	d2.operator=(d1);
	d2 = d1;

2. 赋值运算符只能重载成类的成员函数不能重载成全局函数

原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。

class Date
{
public:
	Date(int year = 1945, int month = 8, int day = 15)
	{
		_year = year;
		_month = month;
		_day = day;
	}


	~Date()
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}
//private:
	int _year;
	int _month;
	int _day;
};


Date& operator=(Date& left, const Date& right)
{
	left._year = right._year;
	left._month = right._month;
	left._day = right._day;

	return left;
}

在这里插入图片描述

3.编译器生成一个默认赋值运算符重载

用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。

注意:
内置类型成员变量是直接赋值的,
而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

四、前置++和后置++重载

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1328, int month = 1, int day = 4)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 前置++
	Date& operator++()
	{
		_day += 1;
		return *this;
	}
	// 后置++
	Date operator++(int)
	{
		Date tmp(*this);
		_day += 1;
		return tmp;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d;
	Date d1(1949, 10, 1);

	d = d1++; // d: 1949 10 1   d1: 1949 10 2
	
	d = ++d1; // d: 1949 10 3   d1: 1949 10 3

	return 0;
}
  • 前置++
    前置++:返回+1之后的结果
    注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
  • 后置++
    前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
    C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递
    注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1而temp是临时对象,因此只能以值的方式返回,不能返回引用

总结

C++拷贝构造函数、运算符重载函数、赋值运算符重载函数、前置++和后置++重载等的介绍。

标签:Date,int,._,month,运算符,++,year,重载,day
From: https://blog.csdn.net/Farewell_me/article/details/139870920

相关文章

  • C++学习笔记----重载运算符
    运算符重载运算符重载可以在通过成员函数或者全局函数进行重载,编译器提供了内置的运算符;我们可以通过定义对应的函数和参数进行重载,也可以使用编译器提供的名称`operator运算符()`进行重载;运算符重载本质是对内置的运算符函数进行重载:函数相同,参数不同;返回引用和地址需要思......
  • 从12个视角看全球C++程序员2/5:深度解读JetBrains最新报告
    讲动人的故事,写懂人的代码4C++程序员最常使用哪种IDE?总体情况(General)VisualStudioCode(VSCode):27%CLion:26%VisualStudio:26%Vi/Vim:4%QtCreator:3%其他工具的使用率较低,分别占据1-2%。嵌入式开发(Embedded)VisualStudioCode:34%CLion:29%VisualStudio:19%......
  • c++ 结构体 联合体 枚举
    结构体:结构体是一种特殊形态的类在C语言中,结构体是一种复合数据类型,用于将多个基本类型或其他复合类型的数据组合成一个整体。结构体中的成员可以是不同的类型,并且可以有不同的访问权限(在C语言中,所有成员默认都是公有的)。在C语言中,结构体不支持成员函数和继承等面向对象的特......
  • c++类的组合
    类的组合描述的是一个类内嵌其他类的对象作为成员的情况,它们之间的关系是一种包含与被包含的关系classEngine{public:voidstart(){//启动引擎的逻辑std::cout<<"Enginestarted!"<<std::endl;}//...其他引擎相关......
  • C++用扩充的结构体类型求复数的绝对值
    结构体的扩充,声明一个扩充的结构体complex,complex结构体中两个双精度数据real和imag,分别代表实数部分和虚数部分 另外还有两个属于结构体的函数:init,asbcomplex,其中init用来给real和imag赋初值,asbcomplex用来计算和返回复数的绝对值real和imag是数据成员函数init和asbcom......
  • 理解C++虚函数和虚表(vtbl)机制
    引言C++是一种强大且灵活的编程语言,它支持面向对象编程(OOP)的各种特性,其中虚函数(virtualfunction)是实现多态性(polymorphism)的关键机制。本文将深入探讨虚函数的原理、虚表(vtbl)的作用,以及这些特性在实际编程中的实现。通过理解这些概念,您将能够更好地掌握C++的多态性和面向......
  • Effective C++ 改善程序与设计的55个具体做法笔记与心得 4
    四.设计与声明18.让接口容易被正确使用,不易被误用请记住:好的接口很容易被正确使用,不容易被误用。你应该在你的所有接口中努力达成这些性质“促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容。“阻止误用”的办法包括建立新类型、限制类型上的操作、束缚......
  • 运算符(简单汇总)
    算数运算符+(加法运算符或正值符号)-(减法运算符或负值符号)*(乘法运算符)/(除法运算符)%(模运算符:求余。如5%3的结果是5除以3所得的余数是2)优先级:先乘除模,后加减,括号最优先(括号里的内容优先级最高)。左结合性:当一个运算对象两边的算术运算符优先级相同时,运算对象先与左......
  • C/C++ 堆栈stack算法详解及源码
    堆栈(stack)是一种常见的数据结构,具有"先进后出"(LastInFirstOut,LIFO)的特性。堆栈算法允许在堆栈顶部进行元素的插入和删除操作。堆栈的操作包括:入栈(Push):将元素添加到堆栈的顶部。出栈(Pop):从堆栈的顶部移除元素。取栈顶元素(Top):获取堆栈顶部的元素,但不对其进行删除操作。......
  • C/C++ stack实现深度优先搜索DFS算法详解及源码
    深度优先搜索(DepthFirstSearch,DFS)是一种图遍历算法,它从一个节点开始,通过访问其相邻节点的方式,依次深入到图中的更深层次。Stack(栈)是一种先进后出(LastInFirstOut,LIFO)的数据结构,它非常适合实现DFS算法。首先,我们来解释一下Stack实现DFS算法的原理。DFS算法的核心思想是......