探索C++对象模型:流插入提取探讨、const修饰、初始化列表(下篇)

#新星杯·14天创作挑战营·第11期#

 

前引:在C++语言长达三十余年的演进历程中,每一次标准更新都在试图平衡性能与抽象、控制与安全之间的微妙关系。从C++11引入的"现代C++"范式开始,开发者得以在保留底层控制能力的同时,借助语言特性大幅提升代码的可维护性与安全性。本文聚焦于类与对象,初始化列表与构造函数究竟有何关系!const又是如何解决隐藏的被修改问题的~

目录

自动识别—探讨

流插入识别自定义类型

流提取识别自定义类型

总结

连续流插入

连续流提取

const成员

问题

解决方法

初始化列表

引入

初始化列表

必须使用初始化列表的情景

(1)const成员初始化

(2)引用成员

(3)无默认构造函数的类型成员

初始化顺序

效率比较


自动识别—探讨

难道编译器可以自动识别内置类型真的是“自动”吗?

这是因为C中需要一律先指明类型,再去打印出来;而C++中是在库里面帮我把准备工作都做完了,我们就可以直接使用了,例如:

 因此我们可以判断,如果需要编译器自动识别自定义类型,那么需要函数重载完成

我们可以看到流插入、流提取的类型是:

流插入识别自定义类型

下面我们通过成员函数重载的方式来实现流插入,但是我们看到如果流插入作参数是无法通过编译

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

双操作数的运算符规定:

第一个参数是左操作数,第二个参数是右操作数。而运算符重载函数的第一个形参被 this 指针隐式占用了,ostream类对象的引用在第二个形参位置,所以在调用运算符重载时,只能是this指针指向的对象在运算符的左边,cout对象在运算符的右边,但这又不符合留提取的含义

这样我们跟库里面的实现是不同的,也感觉到不适。解决方法是在成员函数外面实现,使用友元

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

友元:对外面定义的函数声明前面加上“friend”即可,这样可以解决无法访问私有成员的问题,友               元设置的位置只要在类里面就可以,无论是私有还是公共

下面我们看实际效果:

流提取识别自定义类型

有了上面的经验,我们这里使用友元快速实现流提取

函数声明:

void operator>>(istream& in, Timedate& St2);

函数实现:

void operator>>(istream& in, Timedate& St2)
{
	in >> St2._year >> St2._month >> St2._day;
}

 效果展示:

总结

 我们用成员函数去实现流插入、提取自定义类型是不符合使用习惯的,因为 this 指针占用了第一个参数位置;因此需要考虑参数位置问题,这就得在类外实现,同时友元解决访问类私有成员问题

连续流插入

可以看到我们自定义识别是无法连续的,这是因为函数实现时我们指定了类型是 void

对于一般的赋值顺序是:从右往左

对于流提取、插入顺序是:从左往右(如:cout<<St2 然后 cout<<St1)

因此我们需要改变函数的返回值问题!

 函数声明:

friend ostream& operator<<(ostream& out, Timedate& St2);

函数实现:

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

 效果展示:

连续流提取

原理和上面的一样,提取顺序也是从左往右,我们直接实现!

函数声明:

istream& operator>>(istream& in, Timedate& St2);

函数实现:

istream& operator>>(istream& in, Timedate& St2)
{
	in >> St2._year >> St2._month >> St2._day;
	return in;
}

const成员

问题

将const修饰的“成员函数”称之为const成员函数

const修饰类成员函数,实际修饰该成员函数 隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。这样一定安全了吗?

例如:

下面这种虽然不能对St2进行修改,但是是可以对 *this 指向进行修改的

bool operator==(const Timedate St2);

解决方法

对于不需要更改的隐含参数(*this),我们可以在函数声明和定义后面加上 const 

例如:

初始化列表

引入

在实例化对象时,不管是编译器还是我们自己,会使用构造函数给成员变量一个合适的初始值。

但是经过构造函数之后,我们还不能将其称为成员变量的初始化:

构造函数中的语句只能称为赋初值,而不能称作初始化

因为初始化只能初始化一次,而构造函数体内可以多次赋值

初始化列表

什么是初始化列表:

初始化列表是构造函数的一部分,用于在对象创建时直接初始化成员变量

初始化列表与构造函数关系:

构造函数初始化有两种方式:函数体赋值、初始化列表

所以二者结合才是完整的初始化哦!

如何写初始化列表:

(1)以一个冒号开始,接着是一个以逗号分隔的数据成员列表

(2)每个 "成员变量" 后面跟一个放在括号中的初始值或表达式

class Mytime
{
public:
	Mytime(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{
		//构造函数体功能部分
	}
 
private:
	int _year;
	int _month;
	int _day;
};
必须使用初始化列表的情景
(1)const成员初始化

const 成员变量必须在声明时初始化,且之后不能修改

class Mytime
{
public:
	Mytime(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{
		//构造函数体功能部分
	}
private:
	const int _year;
	int _month;
	int _day;
};

否则就会报错:

(2)引用成员

引用必须在声明时绑定到某个对象

class Mytime
{
public:
	Mytime(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{
		//构造函数体功能部分
	}
private:
	//const成员
	const int _year;
	//引用
	int& _month;
	int _day;
};

但是我们给初始化时传来的参数用引用类型 ,原因如下: 

(1)引用是变量的别名,加上引用就直接是外部的值给成员里面的引用进行赋值

(引用可以赋值引用)

原因:引用的赋值实际是对原对象的赋值,并不修改原来的绑定关系 (多个引用可指向同一象)

(2)如果不用引用,那么初始化函数结束,变量也就销毁了,引用无对象了

正确写法应该是:

(3)无默认构造函数的类型成员

首先我们知道尽管自己不写构造函数,编译器也会调用自己的构造函数,但是它区分内置、自定义

在初始化时:默认构造不会对自定义类型进行处理

class Mytime
{
public:
	Mytime(int year, int month)
		:_year(year)
		, _month(month)
		, newnode(((Mytime*)malloc(sizeof(Mytime) * 5)))
	{
		//构造函数体功能部分
		//对空间进行判断
		if (newnode == nullptr)
		{
			perror("空间失败\n");
			return;
		}
	}
private:
	//const成员
	const int _year;
	//引用
	int& _month;
	//自定义成员
	Mytime* newnode;
};

初始化顺序

初始化列表中成员的初始化顺序与类中成员声明的顺序是一致的,而非初始化列表顺序

例如:

效率比较

对于自定义类型:

初始化列表:直接调用成员的构造函数 

构造函数体内赋值:先调用默认构造函数,再进行赋值

对于内置类型:

无明显差异

                                                   【雾非雾】期待与你的下次相遇!  

评论 40
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值