【C++】类和对象——拷贝构造函数、运算符重载、日期类实现、const成员、取地址操作符重载

目录

    • 拷贝构造函数
    • 运算符重载
    • 日期类实现
    • const成员
    • 取地址及const取地址操作符重载

拷贝构造函数

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

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

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

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(Date& d)//拷贝构造函数要加引用
	{
		cout << "Date(Date& d)" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void print()
	{
		cout << _year << "/" << _month << "/" << _day << "/" << endl;
	}
private:
	int _year = 1;
	int _month;
	int _day;

};
void func1(Date d2)
{

}
int main()
{
	Date d1;
	//以下两种写法都是拷贝构造
	Date d2(d1);
	func1(d2);
	Date d3 = d1;
	
	return 0;
}

C++中规定,传值和传参都要调用拷贝构造函数,C++中不能像C语言一样直接进行传递,自定义类型传值传参必须要调用拷贝构造函数。

【C++】类和对象——拷贝构造函数、运算符重载、日期类实现、const成员、取地址操作符重载_第1张图片

typedef int DataType;
class Stack
{
public:
	//构造函数进行初始化
	Stack(size_t capacity = 3)
	{
		cout << "Stack(size_t capacity = 3)" << endl;
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (_array == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_capacity = capacity;
		_size = 0;
	}
	//拷贝构造函数 ——深拷贝
	Stack(Stack& s)
	{
		cout << "Stack(Stack& s)" << endl;
		_array = (DataType*)malloc(sizeof(DataType) * s._capacity);
		if (_array == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		memcpy(_array, s._array, sizeof(DataType) * s._size);
		_size = s._size;
		_capacity = s._capacity;
	}
	void Push(int data)
	{
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_array);
		_array = nullptr;
		_size = 0;
		_capacity = 0;
	}
private:
	DataType* _array;
	int _size;
	int _capacity;
};
int main()
{
	Stack s1;
	Stack s2 = s1;//调用拷贝构造
	//析构两次
	return 0;
}

在这里插入图片描述

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

内置类型:值拷贝

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//Date(const Date& d)//拷贝构造函数要加引用
	//{
	//	cout << "Date(Date& d)" << endl;
	//	_year = d._year;
	//	_month = d._month;
	//	_day = d._day;
	//}
	void print()
	{
		cout << _year << "/" << _month << "/" << _day << "/" << endl;
	}
private:
	int _year = 1;
	int _month;
	int _day;

};
int main()
{
	Date d1;
	Date d2 = d1;

	return 0;
}

自定义类型:调用他的拷贝构造函数

typedef int DataType;
class Stack
{
public:
	//构造函数进行初始化
	Stack(size_t capacity = 3)
	{
		cout << "Stack(size_t capacity = 3)" << endl;
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (_array == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_capacity = capacity;
		_size = 0;
	}
	//拷贝构造函数 ——深拷贝
	Stack(const Stack& s)
	{
		cout << "Stack(Stack& s)" << endl;
		_array = (DataType*)malloc(sizeof(DataType) * s._capacity);
		if (_array == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		memcpy(_array, s._array, sizeof(DataType) * s._size);
		_size = s._size;
		_capacity = s._capacity;
	}
	void Push(int data)
	{
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_array);
		_array = nullptr;
		_size = 0;
		_capacity = 0;
	}
private:
	DataType* _array;
	int _size;
	int _capacity;
};
int main()
{
	Stack s1;
	Stack s2 = s1;//调用拷贝构造
	//析构两次
	return 0;
}

总结:Date不需要我们实现拷贝构造,默认生成就可以用
Stack需要我们自己实现深拷贝的拷贝构造,默认生成会出现问题

默认生成的构造和析构函数:
1、内置类型不做处理
2、自定义类型调用默认构造和析构函数
默认生成拷贝和赋值重载函数:
1、内置类型值拷贝 (浅拷贝)
2、自定义类型调用拷贝构造赋值重载函数

运算符重载

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

注意:
1、不能通过连接其他符号来创建新的操作符:比如operator@
2、重载操作符必须有一个类类型的参数(自定义类型的参数)
3、用于内置类型的运算符,其含义不能改变,例如内置的整型+,不能改变其含义
4、作为类成员函数重载时,其形参看起来比操作数数目少1,是因为成员函数的第一个参数为隐藏的this
5、. * : : sizeof ?: . 以上5个运算符不能够发生重载。
6、不能改变操作符的操作数个数,一个操作符是几个操作数,那么重载的时候就有几个参数。

重载<运算符

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date(Date& d)//拷贝构造函数要加引用
	{
		cout << "Date(Date& d)" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void print()
	{
		cout << _year << "/" << _month << "/" << _day << "/" << endl;
	}

	//d1
	//d1.operator<(d2)
	bool operator<( const Date& d)
	{
		if (_year < d._year)
		{
			return true;
		}
		else if (_year == d._year && _month < d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day < d._day)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
private:
	int _year = 1;
	int _month;
	int _day;

};

 //运算符重载

int main()
{
	Date d1(2023, 7, 21);
	Date d2(2022, 8, 21);
	//d1 < d2;
	cout << (d1 < d2) << endl;
	cout << d1.operator<(d2) << endl;
}
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
	void print()
	{
		cout << _year << "/" << _month << "/" << _day << "/" << endl;
	}

	//d1
	//d1.operator<(d2)
	bool operator<( const Date& d)
	{
		if (_year < d._year)
		{
			return true;
		}
		else if (_year == d._year && _month < d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day < d._day)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	bool operator==(const Date& d)
	{
		return _year == d._year && _month == d._month && _day == d._day;
	}
	//d1<=d2
	//this就是d1的地址
	bool operator<=(const Date& d)
	{
		return *this < d || *this == d;
	}

	bool operator>=(const Date& d)
	{
		return !(*this < d);
	}
	bool operator>(const Date& d)
	{
		return !(*this <= d);
	}
	bool operator!=(const Date& d)
	{
		return !(*this == d);
	}

	int GetMonthDay(int year, int month)
	{
		int monthArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		//如果是2月才去判断闰年
		if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
		{
			return 29;
		}
		return monthArray[month];
	}

	//出了作用域对象还在可以用引用返回
	Date& operator+=(int day)
	{
		_day += day;
		while (_day > GetMonthDay(_year, _month))
		{
			//月进位
			_day -= GetMonthDay(_year, _month);
			_month++;

			//月满了
			if (_month == 13)
			{
				_year++;
				_month = 1;
			}
		}
		return *this;
	}

	//出了作用域对象不在了,不能用引用返回
	Date operator+(int day)
	{
		Date tmp(*this);
		tmp += day;
		return tmp;
		//while (tmp._day > GetMonthDay(tmp._year, tmp._month))
		//{
		//	//月进位
		//	tmp._day -= GetMonthDay(tmp._year, tmp._month);
		//	tmp._month++;

		//	//月满了
		//	if (tmp._month == 13)
		//	{
		//		tmp._year++;
		//		tmp._month = 1;
		//	}
		//}
	
	}
private:
	int _year = 1;
	int _month;
	int _day;

};

 //运算符重载

int main()
{
	Date d1(2023, 7, 21);
	Date d2(2022, 8, 21);
	//d1 < d2;
	cout << (d1 < d2) << endl;
	cout << d1.operator<(d2) << endl;

	cout << (d1 == d2) << endl;


	//Date ret = d1 += 100;//拷贝构造,返回值
	//ret.print();
	//d1.print();


	Date ret = d1 + 100;
	ret.print();
	d1.print();
	return 0;
}

日期类实现

日期类实现采用声明和定义分离的方式
Date.h文件中存放声明
Date.cpp中存放成员函数的实现

在日期类中如果使用全缺省参数的形式提供构造函数,那么在Date.h中给全缺省参数,Date.cpp中不给

【C++】类和对象——拷贝构造函数、运算符重载、日期类实现、const成员、取地址操作符重载_第2张图片
【C++】类和对象——拷贝构造函数、运算符重载、日期类实现、const成员、取地址操作符重载_第3张图片
认清拷贝构造和赋值的区别:
拷贝构造:一个已经存在的对象去初始化另一个要创建的对象。

int main()
{

	Date d1(2023, 7, 31);
	Date d2(d1);
	return 0;
}

赋值:两个已经存在的对象进行拷贝

int main()
{

	Date d1(2023, 7, 31);
	Date d2(2023, 4, 2);
	d1 = d2;
	return 0;
}

运算符重载,前置++和后置++
前置++:

//++d1  -> d1.operator++()
	Date operator++()
	{

	}

后置++:

//d1++  -> d1.operator++(0)
	Date operator++(int)
	{

	}

1、前置++运算符重载和后置++运算符重载构成了函数重载
2、为了区分前置++和后置++,在后置++中加一个int参数进行占位
3、前置++和后置++两者本质,后置++调用,编译器进行特殊处理

日期类实现:
Date.h文件:

#pragma once
#include 
using namespace std;
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1);
	void Print();
	int GetMonthDay(int year, int month);
	//赋值运算符重载
	Date& operator=(const Date& d);
	bool operator<(const Date& d);
	bool operator==(const Date& d);
	//d1<=d2
	//this就是d1的地址
	bool operator<=(const Date& d);

	bool operator>=(const Date& d);
	bool operator>(const Date& d);
	bool operator!=(const Date& d);
	

	//出了作用域对象还在可以用引用返回
	Date& operator+=(int day);
	//出了作用域对象不在了,不能用引用返回
	Date operator+(int day);

	//出了作用域对象还在可以用引用返回
	Date& operator-=(int day);
	//出了作用域对象不在了,不能用引用返回
	Date operator-(int day);

	//++d1  -> d1.operator++()
	Date& operator++();


    //d1++  -> d1.operator++(0)
	Date operator++(int);

	Date& operator--();
	Date operator--(int);

	//计算两个日期之间差多少天
	int operator-(const Date& d);

private:
	int _year;
	int _month;
	int _day;
};

Date.cpp文件:

#include "Date.h"

//构造函数要对日期进行相应的检查,避免有不存在的时间
Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;

	//检查日期是否合法
	if (month < 1 || month>12
		|| day<1 || day>GetMonthDay(year, month))
	{
		cout << "非法日期" << endl;
		//exit(-1);
	}
}

void Date::Print()
{
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
int Date:: GetMonthDay(int year, int month)
{
	//加static,因为该函数避免不了重复调用,减少开销
	static int monthArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	//如果是2月才去判断闰年
	if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
	{
		return 29;
	}
	return monthArray[month];
}
Date& Date:: operator=(const Date& d)
{
	if (this != &d)
	{
		this->_year = d._year;
		this->_month = d._month;
		this->_day = d._day;
	}
	return *this;//this出了作用域还在,所以用引用返回
}
bool Date:: operator<(const Date& d)
{
	if (_year < d._year)
	{
		return true;
	}
	else if (_year == d._year && _month < d._month)
	{
		return true;
	}
	else if (_year == d._year && _month == d._month && _day < d._day)
	{
		return true;
	}
	else
	{
		return false;
	}
}
bool Date::operator==(const Date& d)
{
	return _year == d._year && _month == d._month && _day == d._day;
}
//d1<=d2
//this就是d1的地址
bool Date:: operator<=(const Date& d)
{
	return *this < d || *this == d;
}

bool Date:: operator>=(const Date& d)
{
	return !(*this < d);
}
bool Date:: operator>(const Date& d)
{
	return !(*this <= d);
}
bool Date:: operator!=(const Date& d)
{
	return !(*this == d);
}



//1、
//出了作用域对象还在可以用引用返回
Date& Date:: operator+=(int day)//自己改变
{
	if (day < 0)
	{
		return *this -= (-day);
	}
	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		//月进位
		_day -= GetMonthDay(_year, _month);
		_month++;

		//月满了
		if (_month == 13)
		{
			_year++;
			_month = 1;
		}
	}
	return *this;
}

//出了作用域对象不在了,不能用引用返回
Date Date:: operator+(int day)//自己不改变
{
	Date tmp(*this);//拷贝构造
	tmp += day;
	return tmp;
	//while (tmp._day > GetMonthDay(tmp._year, tmp._month))
	//{
	//	//月进位
	//	tmp._day -= GetMonthDay(tmp._year, tmp._month);
	//	tmp._month++;

	//	//月满了
	//	if (tmp._month == 13)
	//	{
	//		tmp._year++;
	//		tmp._month = 1;
	//	}
	//}

}



//2、效率上不好
//出了作用域对象还在可以用引用返回
//Date& Date:: operator+=(int day)//自己改变
//{
//	*this = *this + day;
//	
//	return *this;
//}
//
出了作用域对象不在了,不能用引用返回
//Date Date:: operator+(int day)//自己不改变
//{
//	Date tmp(*this);//拷贝构造
//	tmp._day += day;
//	//return tmp;
//	while (tmp._day > GetMonthDay(tmp._year, tmp._month))
//	{
//		//月进位
//		tmp._day -= GetMonthDay(tmp._year, tmp._month);
//		tmp._month++;
//
//		//月满了
//		if (tmp._month == 13)
//		{
//			tmp._year++;
//			tmp._month = 1;
//		}
//	}
//	return tmp;
//
//}

//出了作用域对象还在可以用引用返回
Date& Date:: operator-=(int day)//无拷贝
{
	if (day < 0)
	{
		return *this += (-day);
	}
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}
//出了作用域对象不在了,不能用引用返回
Date Date:: operator-(int day)
{
	Date tmp(*this);
	tmp -= day;
	return tmp;
}


//++d1  -> d1.operator++()
Date& Date:: operator++()
{
	*this += 1;
	return *this;
}


//d1++  -> d1.operator++(0)
Date Date:: operator++(int)
{
	Date tmp(*this);
	*this += 1;
	return tmp;
}


Date& Date:: operator--()
{
	*this -= 1;
	return *this;
}
Date Date:: operator--(int)
{
	Date tmp(*this);
	*this -= 1;
	return tmp;
}

//计算两个日期之间差多少天
//d1 - d2
int Date:: operator-(const Date& d)
{
	Date max = *this;
	Date min = d;   //假设
	int flag = 1;
	if (*this < d)
	{
		max = d;
		min = *this;
		flag = -1;
	}
	int n = 0;
	while (min != max)
	{
		min++;
		n++;
	}
	return n * flag;
}

test.cpp文件:

#include "Date.h"
void TestDate1()
{
	Date d1(2023, 8, 1);
	Date d2;

	d1.Print();
	d2.Print();

	Date d3(2023, 2, 29);
	d3.Print();
}
void TestDate2()
{
	Date d1(2023, 8, 6);
	Date d2(2023, 8, 13);
	Date d3(2023, 8, 13);
	//d1 = d2 = d3;
	d1 += 100;
	d1.Print();
	//拷贝构造
	Date ret = d2 + 200;//也是拷贝构造
	ret.Print();

	//赋值
	/*Date ret;
	ret = d2 + 100;*/


	//Date ret1 = d1;//虽然有赋值,但其实是拷贝构造
	//Date ret(d2 + 100);


}
void TestDate3()
{
	Date d1(2023, 4, 3);
	Date ret = d1 + 100;
	ret.Print();
}
void TestDate4()
{
	Date d1(2023, 7, 27);
	d1 -= 2000;
	d1.Print();

	Date d2(2023, 7, 27);
	d2 += -200;
	d2.Print();

	Date d3(2023, 7, 27);
	d3 -= -200;
	d3.Print();
}

//int main()
//{
//
//	Date d1(2023, 7, 31);
//	Date d2(2023, 4, 2);
//	d1 = d2;
//	return 0;
//}

void TestDate5()
{
	Date d1(2023, 7, 27);
	//Date ret1 = d1++; //后置++返回++之前的值,之后自己变成++之后的值
	Date ret1 = d1.operator++(0);
	ret1.Print();
	d1.Print();
	

	//Date ret2 = ++d1;
	Date ret2 = d1.operator++();
	ret2.Print();//前置++返回++之后的值,自己也是++之后的值
	d1.Print();
}
void TestDate6()
{
	Date d1(2023, 7, 27);
	Date d3(2023, 10, 1);
	cout << d3 - d1 << endl;
}
int main()
{

	//TestDate1();
	//TestDate2();
	//TestDate3();
	TestDate6();
	return 0;
}

const成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针表明在该成员函数中不能对类的任何成员进行修改

【C++】类和对象——拷贝构造函数、运算符重载、日期类实现、const成员、取地址操作符重载_第4张图片
修改:
【C++】类和对象——拷贝构造函数、运算符重载、日期类实现、const成员、取地址操作符重载_第5张图片

【C++】类和对象——拷贝构造函数、运算符重载、日期类实现、const成员、取地址操作符重载_第6张图片

void TestDate7()
{
    // 权限平移
	const Date d1(2023, 8, 2);
	d1.Print();
	
	//权限缩小
	Date d2(2023, 4, 2);
	d2.Print();
}
void Date::Print() const
{
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
  1. const对象可以调用非const成员函数吗?不能
  2. 非const对象可以调用const成员函数吗?可以
  3. const成员函数内可以调用其它的非const成员函数吗?不能
  4. 非const成员函数内可以调用其它的const成员函数吗?可以

两个函数构成函数重载,一个函数只读,另一个函数只写(或读)


#include 
#include 
using namespace std;
struct SeqList
{
public:
	void PushBack(int x)
	{
		_a[_size++] = x;
	}
	size_t size() const
	{
		return _size;
	}
	const int& operator[](size_t i) const//读
	{                             //函数重载
		assert(i < _size);
		return _a[i];
	}
	int& operator[](size_t i)//可进行s[i]++(写)/读
	{
		assert(i < _size);
		return _a[i];
	}
private:
	int* _a = (int*)malloc(sizeof(int) * 10);
	size_t _size = 0;
	size_t _capacity = 0;
};
void Print(const SeqList& s)
{
	for (size_t i = 0; i < s.size(); i++)
	{
		//s[i]++;
		cout << s[i] << " ";
		//cout << s.operator[](i) << " ";

	}
	cout << endl;
}
int main()
{
	SeqList s;
	s.PushBack(1);
	s.PushBack(2);
	s.PushBack(3);
	s.PushBack(4);

	Print(s);
	for (size_t i = 0; i < s.size(); i++)
	{
		s[i]++;
	}
	Print(s);

	
	cout << endl;
	return 0;
}

哪些成员函数加const最好?
只读函数可以加const,内部不涉及修改成员的(本身不被修改的)都可以加const

取地址及const取地址操作符重载

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。

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


	//取地址&重载有两个版本
	Date* operator&()
	{
		return this;
	}
	const Date* operator&() const
	{
		return this;
	}


private:
	int _year;
	int _month;
	int _day;

};
int main()
{
	Date d1(2023, 2, 3);
	cout << &d1 << endl;
	return 0;
}

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如不想让别人获取到指定的内容!

1、普通对象和const修饰的对象都不想让别人获取到地址

Date* operator&()
	{
		return nullptr;
	}
	const Date* operator&() const
	{
		return nullptr;
	}

2、普通对象可以获取到地址,const修饰的对象不想让别人获取到地址

Date* operator&()
	{
		return this;
	}
	const Date* operator&() const
	{
		return nullptr;
	}

你可能感兴趣的:(cpp,c++)