C++流插入提取,const成员函数和初始化列表学习

目录

流插入和流提取

流插入<<

问题提出:

解决方案1

友元函数(friend)

解决方案2

 流提取

运算符重载知识小补充

const成员函数

 问题提出1

分析 

解决办法

问题提出2

分析

解决办法

取地址重载(一般都不会写)

 再学构造函数

初始化列表

 为什么要用初始化列表

什么是初始化列表

初始化列表的顺序

 


流插入和流提取

一般来说自定义类型是不支持运算符的,想支持的话就必须运算符重载

其中重载流插入的其中一个对象便是cout

cin和cout分别是istream和ostream类型的一个对象,它是在iostream那个头文件里定义的

这些都是C++已经写好在库里面的众多重载函数,所以说cout能支持内置类型

 

流插入<<

问题提出:

我们通常一开始会这样运算符重载<<

void operator<<(ostream& out){
    out<<_year<<"年"<<_month<<"月"<<_day<<"日"<<endl;
}

因为双操作运算符,第一个参数是左操作数,第二个参数是右操作数

结果便是报错

究其原因就是在Date类中,this指针已经悄悄的将第一个参数,即左参数给占用了

所以应该变为下述所讲

 

但是此种方式又不适合我们平常所用的,不符合可读性    

解决方案1

将此重载函数放到全局的位置,让ostream对象放到第一个位置,第二个位置才是自定义类型

    

但这又会出现另一个问题,就是类外面不能访问私有成员,所以又会报错

改正的方法是:将private暂时屏蔽

但是这样私有成员变得将不再安全

所以此种解决方案是不合理的 

 

介绍另一种方法之前,先介绍一个新的东西:友元函数

友元函数(friend)

加了友元之后,便可在不是某类里面访问某类的私有成员

就相当于,声明了这个友元函数是这个类的好朋友,这个类相信它,允许它访问

解决方案2

第二种解决方案就是在类里面加上一个友元声明

	friend void operator<<(ostream& out, const Date& d);

为了能够连续的输出自定义类型,即能做到cout<<d1<<d2<<endl; 

所以应该将其的返回值变为 ostream!

//类里
friend ostream& operator<<(ostream& out, const Date& d);

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

 流提取

流提取的第二个参数便不能加const了,因为他要走到自定义类型里面去

//类里
friend istream& operator>>(istream& in, Date& d);

//类外
istream& operator>>(istream& in, Date& d) {
	in >> d._year >> d._month >> d._day;
	return in;
}

流本质是为了解决,自定义类型输入和输出的问题,因为普通的printf,scanf是无法解决复杂的自定义类型输入和输出的问题的

运算符重载知识小补充

  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型函数
  3. 用于内置类型的运算符,其含义不会改变
  4. 作为类成员函数重载的时候,其形参看起来比操作数少1,是因为隐藏了this指针这个形参
  5. 有5个运算符是不能够重载的
    1. 去成员变量的点 .   
    2. 域作用限定符 ::
    3. sizeof
    4. 三目运算符?:
    5. .* (点星)

*是可以重载的

const成员函数

在正常情况下,下述代码是没有任何问题的

Date d1(2024,7,17);
d1.print();

 问题提出1

但当我们变成这样呢

const Date d1(2024,7,17);
d1.print();

出现了错误

分析 

我们把print()函数拿过来综合对比

	void print() {
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	const Date d1(2024, 7, 16);
	d1.print(); 

 

        但this指针是隐藏的,无法将它找出来加上const

解决办法

在成员函数的后面加const,来弥补无法直接在Date* const this 的前头加const的缺点,且这是规定

	void print() const{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	const Date d1(2024, 7, 16);
	d1.print(); 

 此时的this就相当于变为 const Date* const this

非const对象也能调用,属于权限缩小

问题提出2

在问题一修正的基础上

第一个编不过,第二个又能编过

分析

 

解决办法

与问题一样,不给权限放大就可以了,加const,一般不会修改成员就加一下const

所以有两个原则

  1. 能定义成const的成员函数都应该定义成const,这样const对象和非const对象都可以调用
  2. 要修改成员变量的成员函数,不能定义成const

 

例如,+=就不能加const

	Date& operator+=(int day) {
		if (day < 0) {
			*this -= (-day);
			return *this;
		}

		_day += day;
		while (_day > getMonthday(_year, _month)) {
			_day -= getMonthday(_year, _month);
			_month++;

			if (_month > 12) {
				_year++;
				_month = 1;
			}
		}
		return *this;
	}

取地址重载(一般都不会写)

取地址重载有两个,但也是默认成员函数,不写也没有什么过多问题

	Date* operator&() {
		return this;
	}

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

 

 再学构造函数

先来看看原本的构造函数

Date(int year = 1900, int month = 1, int day = 1) {
	_year = year;
	_month = month;
	_day = day;

	if (_year < 1 || _month>13 || _month<1 ||
		_day>getMonthday(_year, _month)) {
		assert(false);
	}
}

在CPP中还给了另外一种定义构造函数的方法 

初始化列表

 为什么要用初始化列表

函数体无法初始化引用成员,const和自定义类型成员没有默认构造

什么是初始化列表

初始化列表是每个成员定义的地方

以前的Date类的三个私有成员变量没有在初始化列表显示定义,但是其实它也是有定义的,只是C++内置类型默认给随机值,而自定义类型会去调用它的默认构造函数

有些自定义成员想要显示定义,即,用自己的数值初始化

如下代码


class A {
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "我已完成A构造" << endl;
	}
private:
	int _a;
};

namespace lhl {
	class Date {
	public:
		Date(int year, int month, int day)
			:_ref(year)
			, _n(1)
			, _aa(10)
		{
			_year = year;
			_month = month;
			_day = day;
		}
	private:
		int _year,_month,_day;
		A _aa;
		int& _ref;
		const int _n;
	};
}

int main() {
	lhl::Date d1(2024, 7, 17);
	return 0;
}

那么有了初始化列表,但是不能只用初始化列表,因为有些初始化或者检查工作,初始化列表也不能全部搞定

初始化列表的顺序

初始化列表初始的顺序,跟初始化列表的顺序无关,只跟声明的顺序有关

namespace lhl2 {
	class A
	{
	public:
		A(int a)
			:_a1(a)
			, _a2(_a1)
		{}

		void Print() {
			cout << "a1的值是" << _a1 << endl;
			cout << "a2的值是" << _a2 << endl;
		}
	private:
		int _a2;
		int _a1;
	};
}

int main() {
	lhl2::A aa(1);
	aa.Print();
	return 0;
}

根据上述表达,可能我们会以为a1和a2的值都是1,1

但是结果并不是

 

显而易见,a1为1,a2为随机值 

为什么呢?

前面说过:初始化列表初始的顺序,跟初始化列表的顺序无关,只跟声明的顺序有关

这里先声明了_a2,后来才声明了_a1

所以先声明_a2,因为_a2(_a1),此时的_a1还没有被赋值,只是一个随机值,所以_a2也会变成随机值

后来声明_a1,_a1(a),a为1,所以_a1为1.

 

以上便是本篇博文的学习内容,如有错误,还望各位大佬指点出来,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值