类和对象(3)

文章目录

  • 1.回顾上节
  • [2. 拷贝构造](#2. 拷贝构造)
  • [3. 运算符重载(非常重要)](#3. 运算符重载(非常重要))
  • [4. 赋值运算符重载](#4. 赋值运算符重载)

1.回顾上节


默认成员函数 :我们不写,编译器自动生成。我们不写,编译器不会自动生成

默认生成构造和析构:

  1. 对于内置类型不做处理
  2. 对于自定义类型会调用对应的构造/析构。

2. 拷贝构造

cpp 复制代码
#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year=1, int month=1, int day=1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//拷贝构造,函数名和类名相同
	//拷贝构造的参数为什么不能是传值?
	//C++自定义类型的成员在这个地方传值需要调用拷贝构造,无穷无尽
	//因此自定义类型必须调用拷贝构造,所以要用引用&。最好加const
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void Print()
	{
		cout << _year << "年" << _month << "月" << _day << "日" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(d1);
	d1.Print();
	d2.Print();//这时改变_year等会改变d1

	return 0;
}

以下为不调用拷贝构造时 ,会默认生成拷贝构造

内置类型会处理,因此日期类不需要自己去写拷贝构造

自定义类型会去调用他的拷贝构造

cpp 复制代码
	Stack st1;
	Stack st2(st1);
	//栈中保持后进先出,后定义的先析构。
	//st1变成野指针。

指向同一块空间的问题:

  1. 插入删除数据会互相影响
  2. 析构两次,程序崩溃。
    默认的拷贝:
    浅拷贝/值拷贝
    **深拷贝:**让各自有各自独立的空间,开另外的空间,把值拷贝下来。
    更深入层次的拷贝
cpp 复制代码
typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
				exit(-1);
		}
		_size = 0;
		_capacity = capacity;
	}
void Push(const DataType& data)
{
	_array[_size] = data;
	_size++;
}
Stack(const Stack& st)//深拷贝
{
	_array = (DataType*)malloc(sizeof(DataType) * st._capacity);
	if (nullptr == _array)
	{
		perror("malloc申请空间失败");
		exit(-1);//直接终止程序
	}
	//拷贝数组空间上的值
	memcpy(_array, st._array, sizeof(DataType) * st._size);
	_size = st._size;
	_capacity = st._capacity;
}
~Stack()
{
	if (_array)
	{
		free(_array);
		_array = nullptr;
		_capacity = 0;
		_size = 0;
	}
}
private:
	DataType *_array;
	size_t _size;
	size_t _capacity;
	};
int main()
{
	Stack st1;
	st1.Push(1);
	st1.Push(2);
	st1.Push(3);
	st1.Push(4);

	Stack st2(st1);
	//栈中保持后进先出,后定义的先析构。
	//没有写拷贝构造,编译器自动生成了一个

	return 0;
}

什么情况下需要写拷贝构造呢?

不能用指针来衡量,如果自己实现了析构函数释放了空间,就需要实现拷贝构造。

  1. 对于内置类型完成浅拷贝/值拷贝--按byte一个个拷贝
  2. 自定义类型 ,去调用这个成员拷贝构造/赋值重载
    2种大方向的特性
cpp 复制代码
typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
				exit(-1);
		}
		_size = 0;
		_capacity = capacity;
	}
void Push(const DataType& data)
{
	_array[_size] = data;
	_size++;
}
Stack(const Stack& st)
{//拷贝构造对内置类型完成值拷贝或者浅拷贝。
	cout << "Stack(const Stack& st)" << endl;
	_array = (DataType*)malloc(sizeof(DataType) * st._capacity);
	if (nullptr == _array)
	{
		perror("malloc申请空间失败");
		exit(-1);//直接终止程序
	}
	//拷贝数组空间上的值
	memcpy(_array, st._array, sizeof(DataType) * st._size);
	_size = st._size;
	_capacity = st._capacity;
}
~Stack()
{
	if (_array)
	{
		free(_array);
		_array = nullptr;
		_capacity = 0;
		_size = 0;
	}
}


private:
	DataType *_array;
	size_t _size;
	size_t _capacity;
	};
//对于自定义类型,不需要写拷贝构造和构造。不写编译器会自动生成构造函数,构造函数符合我们的需求
class MyQueue
{
public:
	//默认生成构造和析构
	//默认生成拷贝构造
private:
	Stack _pushST;
	Stack _popST;
	int _size = 0;//缺省值,用缺省值处理
};
int main()
{
	Stack st1;
	st1.Push(1);
	st1.Push(2);
	st1.Push(3);
	st1.Push(4);

	Stack st2(st1);
	//栈中保持后进先出,后定义的先析构。
	//没有写拷贝构造,编译器自动生成了一个
	MyQueue q1;
	//调用了拷贝构造
	MyQueue q2(q1);
	return 0;
}

那些场景存在拷贝构造

Date d2(d1);

Date d3=d1;//拷贝构造

传返回值的过程中能用引用就用引用 ,减少拷贝。除非就是想让他自己调用拷贝构造,拷贝一份独立的出来

参数基本都可以用引用,返回值不一定。局部对象不能用引用。

cpp 复制代码
Date Test(Date d)
{
  Date temp(d);
  return temp;
}

3. 运算符重载(非常重要)

为了增强程序的可读性 ,是具有特殊函数名 的函数,也有其返回值类型函数名字 以及参数列表,其返回值类型与参数列表与普通的函数类似

  • 比较日期大小
    内置类型可以 比较大小,自定义类型不可以
  • 运算符重载和函数重载无关:
    函数重载是支持参数名相同,参数不同的函数 ,随时可以用
    运算符重载:自定义类型 对象可以使用运算符。
    两个地方都用了重载,但两个地方没有关联
  • 运算符重载:实现一个函数。新增一个关键字operator加操作符有参数有返回值
  • 参数和返回值根据运算符确定。有的有返回值有的没有。
  • 运算符有几个操作数 就有几个参数
cpp 复制代码
class Date
{
public:
	Date(int year=1, int month=1, int day=1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//private:
	int _year;
	int _month;
	int _day;
	
};

bool operator==(const Date& d1, const Date& d2)//运算符重载可以实现在全局。
{
	return d1._year == d2._year
		&& d1._month == d2._month
		&& d1._day == d2._day;
}
int main()
{
	Date d1(2023, 9, 14);
	Date d2(2023, 9, 14);

	cout<<operator==(d1, d2)<<endl;
	cout <<( d1 == d2) << endl;//全局函数,转换成调用这个函数operator==(d1,d2);和上一行一样
	
	//运算符优先级<<高于==
	return 0;
}

当放成私有时

cpp 复制代码
class Date
{
public:
	Date(int year=1, int month=1, int day=1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
	
};

直接把函数放在类里面。类外面受到访问限定符的限制,放到类里面就解决问题了。但是会报错

其中还有隐藏的参数(2个):this

成员函数调用的方式也不同了。

cpp 复制代码
//d1==d2转换为d1.operator==(d2)
bool operator==(const Date& d)
	{
	//this:d1;d:d2
		return this->_year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
cpp 复制代码
	cout<<d1.operator==(d2)<<endl;
	cout <<( d1 == d2) << endl;//成员函数转换成调用这个函数d1.operator==(d2);和上一行一样
	


运算符重载

  1. 函数名:operator+运算符或操作符
  2. 返回值类型/参数:根据需求调用
  3. 不能乱接其他符号创造一个新的操作符,如:operator@
  4. 必须有一个类类型参数**(自定义类型)**
  5. 不能对内置类型重载,其含义不能改变。如内置类型的整型------,不能改变其含义
  6. 作为类成员函数重载时,其形参看起来比操作数目少1,因为成员函数的第一个参数为隐藏的this
  7. .* :: sizeof ?:(三目运算符) .(成员访问) 注意以上5个运算符不能重载,这个经常在笔试选择题中出现
cpp 复制代码
//b1<b2小测
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;
    }
}

//b1<=b2复用,根据上面有<有=

cpp 复制代码
bool operator<=(const Date& d)
{
	return *this < d || *this == d;
}

//b1>b2,取反。

cpp 复制代码
bool operator>(const Date& d)
{
	return !(*this <= d) ;
}

4. 赋值运算符重载

d1=d2;//是一种拷贝

cpp 复制代码
//d1=d2
void operator=(const Date& d)//不用引用不会无穷递归,但会白白走一次拷贝构造,所以最好加上引用
{

	_year = d._year;
	_month = d._month;
	_day = d._day;
}
d3=d2=d1;//编译不通过d1赋值给d2,d2的返回值传给d3

连续赋值,从右往左赋值。i=j=k; k赋值给j,返回j

cpp 复制代码
Date& operator=(const Date& d)
{

	_year = d._year;
	_month = d._month;
	_day = d._day;
	//*this是d1
	return *this;
	//出了作用域还在,应该加引用。
	//返回值是为了支持连续赋值,保持运算符的特性。
}

d1=d1

自己给自己赋值,可以加一个判断

cpp 复制代码
Date& operator=(const Date& d)//引用
{
 if(this!=&d)//取地址,this是左操作数的地址,d是右操作数的别名,地址相同则不用自己给自己赋值
 {
	_year = d._year;
	_month = d._month;
	_day = d._day;
 }
	return *this;
}

+=支持连续赋值,只要支持连续赋值就都有返回值。。

前置++,d1.operator();

后置++,d2.operator(int);

int仅仅是为了占位,和牵制重载区分

cpp 复制代码
//++d1;
Date& Date::operator++()
{
  Date tmp(*this);
  *this+=1;
  return tmp;
}
cpp 复制代码
//d1++
Date Date::operator++(int)
{
  Date tmp(*this);
 *this+=1;
 return tmp;
}

对于内置 类型,前置和后置++没有区别

对于自定义类型,**前置++**效率高,后置++还要拷贝

相关推荐
火云洞红孩儿15 分钟前
基于AI IDE 打造快速化的游戏LUA脚本的生成系统
c++·人工智能·inscode·游戏引擎·lua·游戏开发·脚本系统
青い月の魔女41 分钟前
数据结构初阶---二叉树
c语言·数据结构·笔记·学习·算法
qq_589568101 小时前
node.js web框架koa的使用
笔记·信息可视化·echarts
FeboReigns1 小时前
C++简明教程(4)(Hello World)
c语言·c++
FeboReigns1 小时前
C++简明教程(10)(初识类)
c语言·开发语言·c++
zh路西法2 小时前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(二):从FSM开始的2D游戏角色操控底层源码编写
c++·游戏·unity·设计模式·状态模式
stm 学习ing2 小时前
HDLBits训练6
经验分享·笔记·fpga开发·fpga·eda·verilog hdl·vhdl
.Vcoistnt2 小时前
Codeforces Round 994 (Div. 2)(A-D)
数据结构·c++·算法·贪心算法·动态规划
小k_不小2 小时前
C++面试八股文:指针与引用的区别
c++·面试
stm 学习ing2 小时前
HDLBits训练4
经验分享·笔记·fpga开发·课程设计·fpga·eda·verilog hdl