类和对象(三)

默认成员函数

接下来继续看剩下的两个默认成员函数。

const成员函数

将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后 ⾯。const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进⾏修改。

先来看例子:

所以C++设计了使用 const 修饰成员函数来达到目的。

所以,只需在成员函数参数列表后添加 const ,就能解决问题。

所以,对于一个类,如果我们希望所有的成员函数都默认为const,即不修改对象状态,我们可以使用const关键字来修饰成员函数。


取地址运算符重载

取地址运算符重载分为普通取地址运算符重载和 const 取地址运算符重载,⼀般这两个函数编译器⾃动 ⽣成的就可以够我们⽤了,不需要去显⽰实现。取地址的意思就是返回当前对象的地址,对于成员函数来讲,this指针就是它的地址。

cpp 复制代码
class Date
{
public:
	//普通取地址运算符重载
	Date* operator&()
	{
		return this;
	}

	//const取地址运算符重载,返回一个const Date* 的指针。
	const Date* operator&() const
	{
		return this;
	}

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

友元

友元就是提供了⼀种突破类访问限定符封装的⽅式,友元分为:友元函数和友元类,在函数声明或者类 声明的前⾯加 friend,并且把友元声明放到⼀个类的⾥⾯。外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数。友元函数可以在类定义的任何地⽅声明,不受类访问限定符限制。

cpp 复制代码
#include<iostream>

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

	//友元函数声明
	friend void func(const Date& d);

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

void func(const Date& d)
{
	std::cout << d._year << "年" << d._month << "月" << d._day << "日" << std::endl;
}

int main()
{
	Date d1;
	func(d1);
	return 0;
}

并且⼀个函数可以是多个类的友元函数。

cpp 复制代码
#include<iostream>

//前置声明,否则Date类中的友元函数不认识A类
class A;

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

	//友元函数声明
	friend void func(const Date& d, const A& a);

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

class A
{
	friend void func(const Date& d, const A& a);

private:
	int _a1 = 1;
	int _a2 = 2;
};


void func(const Date& d, const A& a)
{
	std::cout << d._year << "年" << d._month << "月" << d._day << "日" << std::endl;
	std::cout << a._a1 << std::endl;
}

int main()
{
	Date d1;
	A a;
	func(d1, a);
	return 0;
}

友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员。

cpp 复制代码
#include<iostream>

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

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

class A
{
public:
	void func(const Date& d)
	{
		std::cout << d._year << "-" << d._month << "-" << d._day << std::endl;
		std::cout << _a1 << ' ' << _a2 << std::endl;
	}
private:
	int _a1 = 1;
	int _a2 = 2;
};

int main()
{
	Date d;
	A a;
	a.func(d);
	return 0;
}

友元类的关系是单向的,不具有交换性。比如A类是B类的友元,但是B类不是A类的友元。而且友元类关系是不能传递,如果A是B的友元,B是C的友元,但是A不是C的友元。


赋值重载流插入和流提取

上一篇的利用运算符重载在类中实现了各种操作(详见:点这里),接下来就在类内实现流插入和流提取的重载(也就是在类内实现自定义类型的输入输出)。

流插入

通过查阅资料可以看到,cin 是 istream 类型的对象,cout 是 ostream 类型的对象,如果要重载,则类型简单可以写为:

但这样写是有问题的,是不对的。

其报错信息显示出,出现这样的错误本质上是因为参数不匹配造成的。

如果改为这样,就不会报错,也能正常运行。

所以,重载 >> 和 << 时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调⽤时就变成了 << cout 对象,不符合使⽤习惯和可读性。重载为全局函数把 ostream/istream 放到第⼀个形参位置就可以了,第⼆个形参位置当类类型对象。

但又出现了新的问题,就是无法访问类内的成员变量。所以我们可以使用C++的 友元 来访问类内部的成员变量。

但其还不支持连续赋值,和=等运算符一样,它一定是要支持连续赋值的,所以还要进行改造。

但与=运算符不同,流插入流提取运算符是从左到右赋值的。所以得:

cpp 复制代码
#include<iostream>
using namespace std;

class Date
{
public:
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	// 拷贝构造函数
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	// 赋值运算符重载
	Date& operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		return *this;
	}

	// 析构函数
	//日期类无要释放的资源,编译器自动生成的析构即可满足要求。

	void Print()
	{
		std::cout << _year << "-" << _month << "-" << _day << std::endl;
	}

    //友元函数声明
	friend ostream& operator<<(ostream& out, const Date& d);

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

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

void test()
{
	Date d1(2024, 6, 6);
	Date d2(2006, 1, 2);
	cout << d1 << d2;
}

int main()
{
	test();
	return 0;
}

接下来就是流提取得重载函数。

流提取

流提取是 istream 类型的对象,所以:

cpp 复制代码
#include<iostream>
using namespace std;

class Date
{
public:
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	// 拷贝构造函数
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	// 赋值运算符重载
	Date& operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		return *this;
	}

	// 析构函数
	//日期类无要释放的资源,编译器自动生成的析构即可满足要求。

	void Print()
	{
		std::cout << _year << "-" << _month << "-" << _day << std::endl;
	}

	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);

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

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

istream& operator>>(istream& in, Date& d)
{
	cout << "请输入年月日:";
	in >> d._year >> d._month >> d._day;
	return in;
}

void test()
{
	Date d1(2024, 6, 6);
	Date d2(2006, 1, 2);
	cin >> d1 >> d2;
	cout << d1 << d2;
}

int main()
{
	test();
	return 0;
}

内部类

如果⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。内部类是⼀个独立的类,跟定义在 全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。并且内部类默认是外部类的友元类。

简单来说就是,内部类可以访问到外部类成员,但外部类不能访问到内部类得成员。

cpp 复制代码
#include<iostream>

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	class A
	{
	public:
		void func(const Date& d)
		{
			std::cout << d._year << "-" << d._month << "-" << d._day << std::endl;
			std::cout << _a1 << ' ' << _a2 << std::endl;
		}
	private:
		int _a1 = 1;
		int _a2 = 2;
	};
	
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;

	Date::A a;

	a.func(d1);

	return 0;
}

匿名函数

直接⽤ 类型 (实参) 定义出来的对象叫做匿名对象,相⽐之前我们定义的 类型 对象名(实参) 定义出来的叫有名对象。匿名对象⽣命周期只在当前⼀⾏,⼀般用于临时定义⼀个对象。

cpp 复制代码
#include<iostream>

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

	void Print()
	{
		std::cout << _year << "-" << _month << "-" << _day << std::endl;
	}

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

int main()
{
	//有名函数
	Date d1;
	d1.Print();

	//匿名函数
	Date().Print();

	return 0;
}
相关推荐
霁月风1 分钟前
设计模式——观察者模式
c++·观察者模式·设计模式
橘色的喵2 分钟前
C++编程:避免因编译优化引发的多线程死锁问题
c++·多线程·memory·死锁·内存屏障·内存栅栏·memory barrier
何曾参静谧38 分钟前
「C/C++」C/C++ 之 变量作用域详解
c语言·开发语言·c++
AI街潜水的八角1 小时前
基于C++的决策树C4.5机器学习算法(不调包)
c++·算法·决策树·机器学习
JSU_曾是此间年少1 小时前
数据结构——线性表与链表
数据结构·c++·算法
此生只爱蛋2 小时前
【手撕排序2】快速排序
c语言·c++·算法·排序算法
何曾参静谧3 小时前
「C/C++」C/C++ 指针篇 之 指针运算
c语言·开发语言·c++
lulu_gh_yu3 小时前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
ULTRA??4 小时前
C加加中的结构化绑定(解包,折叠展开)
开发语言·c++
凌云行者4 小时前
OpenGL入门005——使用Shader类管理着色器
c++·cmake·opengl