C++类(5)

news2024/11/13 10:39:00

1.<<和>>操作符重载

我们该如何重载操作符<<和>>呢?

如果在类里面,

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

好像这样就可以了,(>>用istream就可以了)

但是当你输入

cout << d1;//编译器报错

编译器就会报错

因为Date对象默认占用第一个参数(this指针),就是做了左操作数

正确做法是

	d1 << cout;
//本质是d1.operator<<(cout)

你也许觉得这样写会很反人人类,那有没有更好的方法呢?

答案当然有,那就是写成全局函数,

2.const成员

我们来思考一个问题

我们在写函数的时候如果不想this指针指向的内容被修改怎么办?

可能我们会说加一个const不久行了!

但是this指针是不能显示写的,C++给我们提供了一种方式

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

 Date d1;
const Date d2;
d1.print();
d2.print();//不加print函数上的const编译会报错,因为这样d2的权限被放大了
           //但是权限只能缩小或者平移,所以编译器会报错

​

我们再来看看下面这个代码

bool Date:: operator>(const Date& x)
{
	if (_year == x._year)
	{
		return true;
	}
	else if ((_year == x._year) && (_month > x._month))
	{
		return true;
	}
	else if ((_year == x._year) && (_month == x._month) && (_day > x._day))
	{
		return true;
	}
	return false;
}


Date d1;
const Date d2;
d1<d2//不会出错 原因是d1<d2本质上是d1.operator<(d2),d1传给*this,权限平移,
     //而d2传给const Date& x权限的平移
d2>d1//编译器会出出错  原因是d2<d1本质上是d2.operator<(d1),d1传给const Date& x权限缩小
      //而d2传给了*this,权限缩小

所以当成员函数后面加上const之后,普通变量和cosnt变量都可以调用

那么能不能所有成员函数都加这个const?

答案肯定不是,有些函数是要改变this指针指向的值,加了cosnt我怎么改值

同时,只要成员函数内部不修改成员变量,都应该加const,这样const对象和普通对象就都可以调用了!

当然这个权限对于指针同样使用

const int a=3;
int *b=a;  //权限放大编译无法通过

 3.取地址重载函数

我们讲了四个默认成员函数,还剩两个默认成员函数

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

	}
public:
	int _year;
	int _month;
	int _day;
};

这两个成员函数无非是对普通对象和const对象的取地址符进行重载,一般不需要我们自己去写,编译器自动生成就可以了,只有特殊情况才需要重载,比如想让别人获得指定内容!

class Date
{
public:
	Date* operator&()
	{
		return nullptr;
		}
	const Date* operator&()const
	{

	}
public:
	int _year;
	int _month;
	int _day;
};

比如说,像下面这样写,你就无法获得普通变量地址,但是可以获得const对象的地址!

4.再谈构造函数

我们如果想给对象的成员一个初始值,有两种方法,第一种就是我们前面学的构造函数体赋值,很熟悉

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

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

今天我们学另外一种,就是初始化列表

1.初始化列表

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

public:
	int _year;
	int _month;
	int _day;
};

2.cosnt成员和引用成员

 首先我们来看引用成员和const成员,这两种都必须在定义的时候初始化

但是类里面的成员函数都是声明

我们在调用类的构造函数体赋值,它的作用是对定义的成员初始化

所以,初始化要在构造函数前,也就是构造函数的{}前(即前面的初始化列表)

初始化列表相当于成员定义的地方

当然对于内置的const类型也可以用缺省参数+构造函数体赋值

3.自定义类型(没有默认构造函数)

自定义成员为啥在该类没有默认构造函数的时候只能用初始化列表?

首先,我们要明确,自定义初始化要调用构造函数,这个时候该类没有默认构造函数,怎么调用?

这个地方的默认构造函数包括,没有显示写编译器自动生成的,无参数的构造函数,全缺省参数的构造函数

什么时候我们没有默认构造函数,我们自己写一个带参不全确省的构造函数的时候

向上面这个代码类a不存在默认构造函数,编译器就不知道怎么处理了

所以这个地方我们只能在初始化列表可以

像这样

4.总结

当然我们不写初始化列表,每个成员也会走初始化列表

只不过里面没内容罢了

​
class Date
{
public:
	
		Date(int year, int month, int day)
	
		:_month(month)
		, _day(4)//这个地方显示写了,就不会去调用初始值了!
		,_year(year)
		{}

public:
	int _year=3;//这个地方的缺省值是给初始化列表的
	int _month=1;//这个地方的缺省值是给初始化列表的
	int _day=3;//这个地方的缺省值是给初始化列表的
};

​

我们更推荐写初始化列表,但是初始化列表也无法解决全部的问题!

比如我malloc开辟一块空间,列表初始化可以帮我们解决

但是它无法帮我们判断malloc是否成功,是否成功的判定只能写在构造函数体内

此外,初始化列表执行的顺序和语句顺序无关,和成员顺序有关

class Date
{
public:
	
		Date(int year, int month, int day)
	
		:_month(month)//(1)
		, _day(day)   //(2)
		,_year(year)  //(3)
 		{}

public:
	int _year;
	int _month;
	int _day;
};

这个地方列表初始化并不是(1)->(2)->(3)

而是(3)->(1)->(2)

因为这个地方成员声明的顺序是

_year ->_month->_day

6.explicit关键字

​
class aa
{
public:
	aa(int s)
		:a(s)
	{}
private:
	int a;
};

int main(void) {
	aa A(1);
	aa B = 2;//整形转换成自定义类型
	return 0;
}

​

我们看这串代码,编译器没报错,那么这个aa B=2;是啥意思?

答案是隐患性类型转换

比如我们前面提过,

int a = 3;
double b = a;//这个地方是隐式类型转换
//会创建一个临时变量 临时变量有const属性
aa B=2;
//这个地方会用2去调用构造函数,生成临时变量是aa类型
//这个临时变量再会拷贝构造给B
//但是许多新的编译器会优化,会用2直接构造

像vs2022这种比较新的编译器就会用2直接构造

当然,如果你不想这个转换发生也很简单

就直接在构造函数前面加一个explicit就可以了 

explicit 关键字的主要作用如下:
防止隐式类型转换:当构造函数被声明为 explicit 时,编译器将不会自动执行隐式类型转换。这意味着,只有在显式地指定类型转换时,才能使用该构造函数进行对象的创建。

class qaz
{
public:
	explicit qaz(int a)
	{
		qwe = a;
	}
private:
	int qwe;
};

int main(void) {
	int ww = 4;
	qaz q1(4);//正确
	q1 = ww;//错误
	q1 = (qaz)ww;//正确
	return 0;
}

explici只能作用于只有一个参数的构造函数,因为我把普通变量转换成类对象,普通变量只有一个数啊 !

5.static

 1.初始化列表只能初始化对象自己的成员,不能初始化全局的,比如static修饰的,

且static修饰变量只能初始化一次!

2.static修饰的变量也不能写在构造函数体内

3.static修饰的值,只能在类外面定义(只能定义在全局,也就是静态区),定义的时候不用加static,类里面的只是声明,所有该类的对象都可以共享

4.static修饰的变量不可以有缺省值,因为缺省值要走初始化列表!但是static修饰的变量不走初始化列表

5.全局变量的劣势,所有地方都可以修改 

6.static修饰的函数(静态成员函数)不可以访问普通的成员,因为没有this指针,你怎么知道是这个类哪个对象成员的成员呢?

7.static修饰的函数(静态成员函数), 没有this指针,访问需要指定类域且受访问限定符(public,private,protect)影响!

8.static修饰的函数(静态成员函数)可以通过具体的类的对象去调用,或者也可以直接用类域去访问

9.public,private,protetc访问限定符对static修饰的变量和函数同样有用

class Date
{
public:

	Date(int year, int month, int day)
		:_month(month)
		, _day(day)
		, _year(year)
		
	{
	}
	public:
	static void cha()

	{
		cout << "cha" << endl;
	}
public:
	static int a;
	int _year = 3;
	int _month = 1;
	int _day = 3;

};
int Date::a = 10;//对应第3点

int main(void) {
	
	class Date b(2005,4,14);
	cout << Date::a << endl;
	b.cha();//对应第8点
	Date::cha();//对应第8点
	return 0;
}

6.友元函数

前面讲过了,我们在类里面写一个operator<<很反人类,那有没有好的解决办法呢?

答案肯定是有的,答案就是友元函数了;

1.友元函数可以直接访问类的私有成员,它是定义在类外面的普通函数,不属于任何类,

但需要在类的内部声明,声明时需要加上friend关键字。(sizeof不算友缘函数大小,同时类的对象也不能调用该函数)

2.友元函数可以访问类的私有和保护成员,但不是类的成员函数

3.友元函数不能用const修斯,不是成员函数,哪来的this指针?

4.友元函数可以在类里面的任意一个地方定义声明,不受类访问符的限制

5.一个函数可以是多个类的友元函数

6.友元函数的调用和普通函数调用原理相同

其实在C++里面友元函数用的不多,因为友元函数会破会封装性

​
class Date
{
public:
 Date(int year, int month, int day)
		:_month(month)
		, _day(day)
		, _year(year)
		
	{}
	
private:
	friend void print(Date& x);//不受访问限定符影响
	int _year = 3;
	int _month = 1;
	int _day = 3;

};
 void print(Date &x)
//友元函数可以访问类里面的私有成员,由于不是类里面的成员,因此不用Date::了
{
	cout <<x._day<<x. _month<<x._year<<endl;
}
int main(void) {
	
	Date d1(2005,4,14);
	print(d1);
	return 0;
}

​

7.友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员

1.友元是关系是单向的,不具有交换性

2.友元关系不能传递,

3.友元关系不能继承

class Time
{
	friend class Date;
public:
	Time(int hour, int minute, int second)
	{
		_hour = hour;
		_minute = minute;
		_second = second;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	friend class Time;
 Date(int year, int month, int day)
		:_month(month)
		, _day(day)
		, _year(year)
		
	{}
public:
 void print(Time& x)
 {
	 cout <<x._hour<<"-" << x._minute << "-" << x._second << endl;
	}
private:
	int _year = 3;
	int _month = 1;
	int _day = 3;
};
int main(void) {
	
	Date d1(2005,4,14);
	Time t1(11, 34, 56);
	d1.print(t1);
	return 0;
}

 8.内部类

概念:如果一个类定义在另一个类的内部,,这个类叫做内部类,内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员,外部类对内部类没有任何优越的访问权限

注意:内部类就是外部类的友元类,但是外部类不是内部类的友元

特性:1.内部类定义在外部类的protect,private,public都是可以的(且会受限定符限制,因为是定义不是声明)

           2.内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名

            3.sizeof(外部类)=外部类,和内部类没有任何关系

           4.类里面定义出来的东西才会被访问限定符限定,但是声明不会

             5.内部类不能直接定义,要A::B b(B是A的内部域)(前提是public的)

class Date
{
public:
	class Time
	{
	public:
		Time(int hour, int minute, int second)
		{
			_hour = hour;
			_minute = minute;
			_second = second;
		}
	private:
		int _hour;
		int _minute;
		int _second;
	};
public:
	friend class Time;
 Date(int year, int month, int day)
		:_month(month)
		, _day(day)
		, _year(year)
		
	{}
private:
	int _year = 3;
	int _month = 1;
	int _day = 3;
};
int main(void) {
	
	Date d1(2005,4,14);
	Date::Time t1(11, 34, 56);
	return 0;
}

8.匿名对象

class Date
{

public:
	friend class Time;
 Date(int year=2000, int month=3, int day=4)
		:_month(month)
		, _day(day)
		, _year(year)
		
	{
	 cout << "hi" << endl;
 }
public:
	void print()
	{
		cout << "hello" << endl;
	}
private:
	int _year = 3;
	int _month = 1;
	int _day = 3;
};
int main(void) {
	Date d1;//不能加括号的原因是和函数的声明无法分清
	Date(2003, 3, 2);//匿名对象 会调用构造函数
	Date().print();//匿名对象函数调用 会调用构造函数 加括号的原因是类型不能调用函数
	//匿名对象即用即销毁,语句执行完销毁
	//匿名对象和普通对象一样传参,只是没有名字
	Date& ra = Date(2004, 1, 09);//匿名对象具有常性
	const	Date&ra=Date(2004,2,3);//const引用延长了生命周期,生命周期在当前函数局部域
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2237432.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MM-Align】学习基于输运的最优对齐动力学,快速准确地推断缺失模态序列

代码地址 - > github传送 abstract 现有的多模态任务主要针对完整的输入模态设置&#xff0c;即每个模态在训练集和测试集中要么是完整的&#xff0c;要么是完全缺失的。然而&#xff0c;随机缺失的情况仍然没有得到充分的研究。在本文中&#xff0c;我们提出了一种新的方…

高精度算法-保姆级讲解

目录 1.什么是高精度算法 2.高精度加法 3.高精度减法 4.高精度乘法 5.高精度除法 &#xff08;高精度除以低精度&#xff09; 6.高精度阶乘&#xff08;n个低精度数相乘&#xff09; 1.什么是高精度算法 高精度算法&#xff08;High Accuracy Algorithm&#xff09;是…

vue大疆建图航拍功能实现

介绍 无人机在规划一块区域的时候&#xff0c;我们需要手动的给予一些参数来影响无人机飞行&#xff0c;对于一块地表&#xff0c;无人机每隔N秒在空中间隔的拍照地表的一块区域&#xff0c;在整个任务执行结束后&#xff0c;拍到的所有区域照片能够完整的表达出一块地表&…

learnopencv系列三:GrabCut和DeepLabv3分割模型在文档扫描应用中的实现

文章目录 一、使用OpenCV实现自动文档扫描1.1 图片预处理1.2 查找轮廓1.3 检测角点1.4 仿射变换1.5 Streamlit Web App1.5.1 设置扫描函数和图像下载链接函数1.5.2 streamlit app1.5.3 测试结果 二&#xff1a;DeepLabv3文档分割2.1 项目背景2.2 合成数据集2.2.1 图像收集与预处…

SQLite的BLOB数据类型与C++二进制存储学习记录

一、BLOB数据类型简介 Blob&#xff08;Binary Large Object&#xff09;是一种用于存储二进制数据的数据类型&#xff0c;在数据库中常用于存储图片、音频和视频等大型&#xff08;大数据量&#xff09;的二进制数据[1-2]。需要注意的是&#xff0c;SQLite中BLOB类型的单对象最…

C# 自己编写web服务

文件后缀响应 "text/html"; 文件后缀响应 "application/json"; httpListenerContext.Response.ContentType 文件后缀响应; httpListenerContext.Response.AppendHeader("Access-Control-Allow-Origin", "*"); // L…

微服务day04

网关 网关路由 快速入门 创建新模块&#xff1a;hm-gateway继承hmall父项目。 引入依赖&#xff1a;引入网关依赖和nacos负载均衡的依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"…

Agent框架调研:19种Agent架构对比分析

代理&#xff08;Agent&#xff09;指能自主感知环境并采取行动实现目标的智能体&#xff0c;即AI作为一个人或一个组织的代表&#xff0c;进行某种特定行为和交易&#xff0c;降低一个人或组织的工作复杂程度&#xff0c;减少工作量和沟通成本。 背景 目前&#xff0c;我们在…

ODOO学习笔记(4):Odoo与SAP的主要区别是什么?

Odoo 和 SAP 都是知名的企业资源规划&#xff08;ERP&#xff09;软件&#xff0c;它们之间存在以下一些主要区别&#xff1a; Odoo与SAP的区别 一、功能特点 功能广度 Odoo&#xff1a;提供了一整套全面的业务应用程序&#xff0c;涵盖了销售、采购、库存管理、生产、会计、…

python之正则表达式总结

正则表达式 对于正则表达式的学习&#xff0c;我整理了网上的一些资料&#xff0c;希望可以帮助到各位&#xff01;&#xff01;&#xff01; 我们可以使用正则表达式来定义字符串的匹配模式&#xff0c;即如何检查一个字符串是否有跟某种模式匹配的部分或者从一个字符串中将与…

【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板

文章目录 一、日志框架介绍1、浅谈与slfj4、log4j、logback的关系2、性能方面3、Slf4j使用方法 二、log4j配置三、log4j2配置1、SpringBoot整合Log4j22、非SpringBoot项目引入的依赖3、log4j2-spring.xml文件&#xff08;Spring项目&#xff09;或log4j2.xml&#xff08;非Spri…

StarUML建模工具安装学习与汉化最新零基础详细教程【一键式下载】(适用于Windows、MacOS系统、Linux系统)

StarUML破解安装下载教程 前言&#xff1a; StarUML破解与汉化安装下载教程&#xff0c;仅供学习研究和交流使用&#xff0c;禁止作为商业用途或其他非法用途&#xff01; 仓库作者&#xff1a;X1a0He&#xff0c;经仓库作者授权使用。 目录 StarUML破解安装下载教程1. 下载…

【网络安全】2.3 安全的网络设计_2.防御深度原则

文章目录 一、网络架构二、网络设备三、网络策略四、处理网络安全事件五、实例学习&#xff1a;安全的网络设计结论 网络设计是网络安全的基础&#xff0c;一个好的网络设计可以有效的防止攻击者的入侵。在本篇文章中&#xff0c;我们将详细介绍如何设计一个安全的网络&#…

IoTDB 与 HBase 对比详解:架构、功能与性能

五大方向&#xff0c;洞悉 IoTDB 与 HBase 的详尽对比&#xff01; 在物联网&#xff08;IoT&#xff09;领域&#xff0c;数据的采集、存储和分析是确保系统高效运行和决策准确的重要环节。随着物联网设备数量的增加和数据量的爆炸式增长&#xff0c;开发者和决策者们需要选择…

如何找到系统中bert-base-uncased默认安装位置

问题&#xff1a; 服务器中无法连接huggingface&#xff0c;故需要自己将模型文件上传 ubuntu 可以按照这个链接下载 Bert下载和使用&#xff08;以bert-base-uncased为例&#xff09; - 会自愈的哈士奇 - 博客园 里面提供了giehub里面的链接 GitHub - google-research/be…

Qt 学习第十六天:文件和事件

一、创建widget对象&#xff08;文件&#xff09; 二、设计ui界面 放一个label标签上去&#xff0c;设置成box就可以显示边框了 三、新建Mylabel类 四、提升ui界面的label标签为Mylabel 五、修改mylabel.h&#xff0c;mylabel.cpp #ifndef MYLABEL_H #define MYLABEL_H#incl…

华为ensp配置bgp(避坑版)

文章目录 前言一、BGP是什么&#xff1f;二、拓扑三、基础配置四、测试五、拓展总结 前言 BGP&#xff08;Border Gateway Protocol&#xff0c;边界网关协议&#xff09;是一种在互联网中使用的路径矢量协议。它主要用于在不同的自治系统&#xff08;AS&#xff09;之间交换路…

QT最新版6.8在线社区版安装教程

访问QT的官网&#xff1a; Qt | Tools for Each Stage of Software Development Lifecycle 点击 Download Try&#xff1a; 点击社区版最新在线安装&#xff1a; 往下翻网页&#xff0c; 点击下载&#xff1a; 开始安装&#xff1a; 使用--mirror进行启动安装程序&#xff1…

鸿蒙多线程开发——Worker多线程

1、概 述 1.1、基本介绍 Worker主要作用是为应用程序提供一个多线程的运行环境&#xff0c;可满足应用程序在执行过程中与主线程分离&#xff0c;在后台线程中运行一个脚本进行耗时操作&#xff0c;极大避免类似于计算密集型或高延迟的任务阻塞主线程的运行。 创建Worker的线…

海量数据迁移:Elasticsearch到OpenSearch的无缝迁移策略与实践

文章目录 一&#xff0e;迁移背景二&#xff0e;迁移分析三&#xff0e;方案制定3.1 使用工具迁移3.2 脚本迁移 四&#xff0e;方案建议 一&#xff0e;迁移背景 目前有两个es集群&#xff0c;版本为5.2.2和7.16.0&#xff0c;总数据量为700T。迁移过程需要不停服务迁移&#…