类和对象(下):流重载、初始化列表、友元


递归何不归:个人主页
个人专栏 : 《C++庖丁解牛》《数据结构详解》

在广袤的空间和无限的时间中,能与你共享同一颗行星和同一段时光,是我莫大的荣幸


前言

书接上回,我们继续讲解类相关的知识,今天为大家带来的是流重载、初始化列表、友元,全文干货满满,希望可以帮到你

文章目录

  • 前言
  • 一、初始化列表
    • [1.1 初始化列表的定义](#1.1 初始化列表的定义)
    • [1.2 初始化列表的使用](#1.2 初始化列表的使用)
    • [1.3 成员变量的初始化](#1.3 成员变量的初始化)
      • [1.3.1 成员变量的初始化流程](#1.3.1 成员变量的初始化流程)
      • [1.3.2 成员变量初始化的顺序](#1.3.2 成员变量初始化的顺序)
    • [1.4 初始化列表的重要性](#1.4 初始化列表的重要性)
      • [1.4.1 const修饰的成员变量](#1.4.1 const修饰的成员变量)
      • [1.4.2 引用类型成员变量](#1.4.2 引用类型成员变量)
      • [1.4.3 没有默认构造函数的自定义类型成员变量](#1.4.3 没有默认构造函数的自定义类型成员变量)
    • [1.5 初始化列表的缺省值](#1.5 初始化列表的缺省值)
      • [1.5.1 构造函数参数缺省](#1.5.1 构造函数参数缺省)
      • [1.5.2 C++11新特性:成员缺省值](#1.5.2 C++11新特性:成员缺省值)
      • [1.5.3 成员缺省值和函数缺省参数的区别](#1.5.3 成员缺省值和函数缺省参数的区别)
    • [1.6 成员变量走初始化列表的顺序](#1.6 成员变量走初始化列表的顺序)
  • 二、流重载
    • [2.1 流重载的意义](#2.1 流重载的意义)
    • [2.2 流重载的实现(Date类为例)](#2.2 流重载的实现(Date类为例))
      • [2.2.1 流重载函数为何不能是成员函数](#2.2.1 流重载函数为何不能是成员函数)
      • [2.2.2 流重载的实现](#2.2.2 流重载的实现)
  • 结语

一、初始化列表

1.1 初始化列表的定义

1、初始化列表是在C++类的构造函数中,用于对成员变量(特别是常量成员和引用成员)进行初始化的专用语法格式

2、初始化列表是在对象创建时对对象进行初始化,逻辑上可以认为他们是同步进行的 (区别于构造函数是在对象创建之后进行初始化(相当于是赋值))

1.2 初始化列表的使用

以⼀个冒号开始,接着是⼀个以逗号分隔的数据成员列表,每个"成员变量"后⾯跟⼀个放在括号中的初始值或表达式

cpp 复制代码
Date(int year = 1900, int month = 1, int day = 1,int n)
	:_year(year)
	, _month(month)
	, _day(day)
	,_ptr(malloc(sizeof(int)*n)
	{ }

初始化列表是可以和构造函数函数体共存的

cpp 复制代码
Date(int year = 1900, int month = 1, int day = 1,int n)
	:_year(year)
	, _month(month)
	, _day(day)
	,_ptr(malloc(sizeof(int)*n)
	{
		 if(nullptr==ptr)
		 {
		 		perror("mallco");
		 }
	}

1.3 成员变量的初始化

1.3.1 成员变量的初始化流程

每个成员变量在初始化列表中只能出现⼀次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。

如果我们写了两个初始化列表,就像当于是将初始化这个操作走了两遍,这时就会报错:

cpp 复制代码
error C2437:  "year" : 已初始化

这里贴上成员变量生命周期图以助理解

初始化列表初始化成员变量的逻辑就像是我们在创建变量的同时初始化

cpp 复制代码
int i=0;
//在逻辑上是相近的

初始化列表的"在创建变量的同时初始化"的特性使得他可以初始化一些构造函数初始化不了的成员变量(后面细讲)

1.3.2 成员变量初始化的顺序

初始化列表中按照成员变量在类中声明顺序进⾏初始化,跟成员在初始化列表出现的的先后顺序⽆关。建议声明顺序和初始化列表顺序保持⼀致。

1.4 初始化列表的重要性

引⽤成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进⾏初始化,否则会编译报错

1.4.1 const修饰的成员变量

const修饰的变量是的值是只能在变量创建时初始化,不可以在其他地方修改,初始化列表就是变量创建的地方,使用初始化列表就是在创建时初始化使用构造函数就是在初始化完成后为变量赋值,显然构造函数是不能满足要求的(违反了const变量不可修改的规则)

cpp 复制代码
class Date{
public:
    Date()
        : _n(1)
private:
    const int _n;
};
int main(){
    Date d1();
}

1.4.2 引用类型成员变量

引用变量必须在创建时初始化 ,而且不初始化在语法上是不被允许的,这就使得初始化列表是他唯一的初始化方式,构造函数是不可能合法的实现对他的初始化的

cpp 复制代码
class Date{
public:
    Date(int& xx)
    : _ref(xx){}
private:    
    int& _ref;
};
int main(){
    int x = 0;
    Date d1(x);
}

1.4.3 没有默认构造函数的自定义类型成员变量

cpp 复制代码
class Time{
public:
     Time(int hour)
     :_hour(hour){}
private:
     int _hour;
};
class Date{
     public:
     :_t(1){}
private:
     Time _t;          
};
int main()
{
     Date d1;
     return 0;
}

对于没有默认构造函数的自定义类型,如果不加处理的话会直接报错 ,我们可以通过_t(1)来将1隐式 传给自定义类型的非默认构造函数 ,从而实现对没有默认构造函数的自定义类型的初始化

1.5 初始化列表的缺省值

1.5.1 构造函数参数缺省

可以在构造函数参数处直接设置缺省值

cpp 复制代码
Date(int& xx, int year = 1900, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
, _n(1)
, _ref(xx)
,_t(1)

1.5.2 C++11新特性:成员缺省值

C++11引入了新特性:可以在成员变量声明处设置缺省值,这个缺省值是给没有在初始化列表中显式初始化的的成员初始化用的

cpp 复制代码
int _year = 1900;
int _month = 1;
int _day = 1;
const int _n = 1;
int& _ref;
Time _t = 1;

1.5.3 成员缺省值和函数缺省参数的区别

先下结论:
这两个缺省方式使用的是完全不同的逻辑

我们先来看一下成员缺省值的调用条件:

没有在初始化列表中显示实现该变量的初始化(也就是没写出来

这时编译器会自动对内置类型直接使用成员缺省值初始化,将该缺省值视为初始化参数,直接调用匹配的构造函数完成构造。

而函数缺省参数的调用条件是:

  • 参数在初始化列表中被显式传给成员变量(也就是写出式子了
  • 参数没有被显式传递(也就是在主函数中少传了这个参数

1.6 成员变量走初始化列表的顺序

二、流重载

2.1 流重载的意义

我们可以看到C++库里面的<<的自动识别对象就是通过重载实现的

这里的返回值是为了实现流的链式调用

我们想要实现自定义类型的自动识别,就要借鉴C++库里面的方案

2.2 流重载的实现(Date类为例)

2.2.1 流重载函数为何不能是成员函数

这里需要注意的是如果直接将函数放在类里面,this指针会抢占函数的左参数,这会导致我们的函数重载效果不好

正常情况下,应该是左参数是cout,如果函数放在类里面,实际调用的时候就得反着调用了

所以我们这里将<<和>>的重载函数当做全局函数,并且在Date类中添加一个友元声明

cpp 复制代码
	friend std::ostream& operator<<(std::ostream& out, const Date& d);
	friend std::istream& operator>>(std::istream& in, Date& d);

友元声明相当于是给了外部函数类中的成员变量的访问权限

2.2.2 流重载的实现

cpp 复制代码
std::ostream& operator<<(std::ostream& out, const Date& d)
{
	out << d._year << "-" << d._month << "-" << d._day;
	return out;
}

std::istream& operator>>(std::istream& in,  Date& d)
{
	while (1)
	{
		cout << "请输入年月日;>";
		in >> d._year >> d._month >> d._day;
		if (d.CheckDate())
		{
			cout << "非法日期:" << d << endl<<"请重新输入!!!"<<endl;
		}
		else
		{
			break;
		}
	}
		
	return in;
}

结语

类和对象还有一部分内容没有讲完,我会以补充篇的形式放出来,非常感谢你可以看我的文章

相关推荐
Trouvaille ~2 小时前
【Linux】UDP Socket编程实战(四):地址转换函数深度解析
linux·服务器·网络·c++·udp·socket·地址转换函数
王老师青少年编程2 小时前
2022信奥赛C++提高组csp-s复赛真题及题解:星战
c++·真题·csp·信奥赛·csp-s·提高组·星战
兩尛2 小时前
2. 两数相加 c++
开发语言·c++
j445566112 小时前
C++中的备忘录模式
开发语言·c++·算法
近津薪荼2 小时前
dfs专题——二叉树的深搜3(二叉树剪枝)
c++·学习·算法·深度优先
卷卷的小趴菜学编程2 小时前
项目篇----仿tcmalloc的内存池设计(page cache)
c++·缓存·单例模式·tcmalloc·内存池·span cache
m0_706653232 小时前
C++中的解释器模式
开发语言·c++·算法
王老师青少年编程2 小时前
2022信奥赛C++提高组csp-s复赛真题及题解:数据传输
c++·数据传输·真题·csp·信奥赛·csp-s·提高组
hetao17338372 小时前
2026-01-29~02-03 hetao1733837 的刷题记录
c++·笔记·算法