类和对象——下

本文介绍了C++中的const成员函数,包括const对象的作用,流运算符的重载与限制,以及初始化列表的使用和优点。重点讨论了const修饰的参数在函数中的权限变化和初始化列表在处理const和引用方面的优势。
摘要由CSDN通过智能技术生成

类和对象——中(2)-CSDN博客


前言

本篇接类和对象——中(2)-CSDN博客

接着讲类和对象的知识,今天主要来了解下,我们的 const成员函数,,重点还是一种新的初始化方式。


回顾:

        上期我们讲到了拷贝构造函数的使用,当我们使用拷贝构造函数的时候,其运行的原理是将一个已存在的对象作为模版创造一个新的对象,但是默认生成的拷贝构造函数只能对内置类型进行拷贝,而自定义类型的数据是我们需要自己写构造函数的,而我们自己写拷贝构造函数的时候需要注意的是,拷贝构造函数的实现是需要使用引用的,具体的原因见类和对象——中(2)-CSDN博客

         当然拷贝构造的使用中还有一个小小的坑,在我们使用拷贝构造的时候,对象中如果有指针的数据,在进行拷贝的时候是不会发生什么问题的,但是在析构函数调用的时候就会发生二次析构的问题,这里大家需要注意。

        运算符重载这方面其实并没有什么过多的点需要注意,大家只要记住运算符重载是给予运算符更多的选择,也就是可以匹配更多的参数,当然还需要注意有少数的运算符并不能进行运算符重载。

一、流运算符

在c++中我们经常使用的运算符<< 、>>流运算符的使用在c++中默认的只能对内置类型进行操作,那么怎么对自定义类型进行操作呢?

下面我们来进行详细的讲解:

#include<iostream>
using namespace std;
class  A
{
public:
	const ostream& operator<<(ostream& out)
	{
		out << year << "/" << mouth << "/" << day << endl;
		return out;
	}

	A()
	{
		year = 2023;
		mouth = 11;
		day = 4;

	}
private:

	int year;
	int mouth;
	int day;

};


int main()
{
	A s1;
	cout << s1;
	s1 << cout;
}

 

 来看看,这里为什么显示的是s1流入cout错误,而cout流入s1却是正确的?这里涉及到了操作符重载的一个小知识,操作符重载规定了,第一个传入的参数是左值,而第二个是右值,当然,可能有细心的人已经隐隐猜到了这个规则,因为如果没有这个规则的存在,编译器是根本无法知道运算顺序的,这里我们将流运算符的重载放到了类中,这样就导致了this指针将第一个参数位置占据,这也是错误的原因,这里我们只需要将运算符重载放到全局即可。

#include<iostream>
using namespace std;
class  A
{
public:
	

	A()
	{
		year = 2023;
		mouth = 11;
		day = 4;

	}
private:

	int year;
	int mouth;
	int day;

};

const ostream& operator<<(ostream& out , A a)
	{
		out << a.year << "/" << a.mouth << "/" << a.day << endl;
		return out;
	}

int main()
{
	A s1;
	cout << s1;

}

 

 那么我们如果将流运算符的重载放到全局中去的话,这里又有一个问题,这里为了类的数据的封装,我们将数据定为私有,但是全局无法访问,怎么办?

这里给大家讲个新知识,友元函数,友元函数的存在能够帮助我们访问类中的私有数据,友元的定义是在函数前面加上一个friend即可,这样表示了,这个函数是当前文件类的友元函数,而友元函数可以访问该类中的私有数据。但是一定要记住,友元函数的声明一定是在类中声明。

#include<iostream>
using namespace std;
class  A
{
public:
	

	A()
	{
		year = 2023;
		mouth = 11;
		day = 4;

	}
	friend const ostream& operator<<(ostream& out, A a);
private:

	int year;
	int mouth;
	int day;

};

 const ostream& operator<<(ostream& out , A a)
	{
		out << a.year << "/" << a.mouth << "/" << a.day << endl;
		return out;
	}

int main()
{
	A s1;
	cout << s1;

}

 

         当然,这里可能有人会有疑问,为什么流运算符会有返回值,那么,大家注意到了没,流运算符是可以连续使用的,所以这里直接返回cout,来进行下一步的流运算。

        还有一点需要注意!这里我们实现的是自定义类型的流运算,但是有人可能会自己又去搞一个+的运算符重载,一定要注意,这里规定了传入参数一定要有一个自定义类型的对象,不要自己搞两个内置类型参数,这会和std库已经实现的发生冲突。

二、const成员函数

1.const对象

代码如下(示例):

猜猜程序运行的结果是什么?

#include<iostream>
using namespace std;
class  A
{
public:
	
	void Funk(A a)
	{
		year = 0;
		cout << "Funk" << endl;
	}

	A()
	{
		year = 2023;
		mouth = 11;
		day = 4;

	}

private:

	int year;
	int mouth;
	int day;

};





int main()
{
	const A s1;
	s1.Funk(s1);

}

 

        在上面我们看见了运行报错,这是为什么呢?看好,我们使用了const来修饰s1,但是我们调用Funk函数,当我们将const s1传入Funk函数的时候,这里发生了权限的变化,注意看好,Funk函数的形参是A a    也就是这里的形参是没有const的,也就是说,这里的形参是可以改变的,这里就发生了权限放大

 

三.初始化列表

        在我们学习了这么多知识后呢,我们的初始化方式也有了不同,这里来给大家讲个新的初始化方法。

        初始化列表使用方法:

#include<iostream>
using namespace std;
class Date
{
public:
	Date(int year, int mouth, int day)
		:_year(year)        //以冒号开始
		, _mouth(mouth)     //以逗号分隔
		, _day(day)
	{
	
	}


	
private:
	int _year;
	int _mouth;
	int _day;
};

那么,为什么我们需要初始化列表呢?其他的初始化方法不行吗?既然存在初始化列表,就有它存在的意义,在我们c++中,声明引用的同时必须初始化,同样,const修饰的变量也是一样的。但是有了初始化列表,我们就可以干一些别的事情,

 

 

#include<iostream>
using namespace std;
class Date
{
public:
	Date(int year, int mouth, int day)
		:_year(year)
		, _mouth(mouth)
		, _day(day)
	{
	
	}


	
private:
	const int _year;
	int _mouth;
	int _day;
};

 我们有了初始化列表之后,我们就可以将const和引用的声明和初始化分离来写了,这就是初始化列表的作用。

但是初始化列表这么有用,为什么我们不全使用它呢?

        初始化列表的缺点也有一些,当我们初始化一些 像指针一样的数据时,我们malloc一个空间,将其地址交给初始化列表,但是我们并不能判断它是不是空指针,所以我们还是需要函数体来处理一些事情。


总结

         初始化列表帮助我们解决了一些像const,引用声明和初始化无法分离的问题,我们也从const函数的使用上面,更加全面的了解了权限的变化的问题,关于初始化的问题,我们最好还是多使用初始化列表来进行初始化操作。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值