C++ 有元 内部类 匿名对象

news2024/10/7 14:22:18

有元

  使用有元就可以突破封装,可以直接对类当中 私有的 成员 成员函数等等进行访问,在某一次上提供了遍历,但是这增大的 耦合性,破坏了封装,所以建议有元不要多用。

所谓耦合性就是 ,某两个 东西的 关系,越紧密,耦合性就越大。其实,我们要实现封装的话,关系紧密不是好的结果。

有元函数

 在类当中使用  friend 关键字修饰一个 在类外 的 普通函数(这个函数不属于任何类),也就是用 friend 关键字 在类当中进行声明,在类外面进行定义,那么这个函数就是这个类的友元函数这个函数就可以访问这个类当中的私有的成员。

 如下面这个例子,因为 成员函数的第一个参数是 对应对象的  this 指针,那么如果我们在 类当中定义重载运算符,第一个参数就必须是 这个对象的this指针,那我们在调用这个函数的时候,左边的变量只能是对象,比如我们重载  "<<" ,那么我们就只能 d1 << cout ,这样写, 如果 cout << d1 ,这样写就会报错。

但是第一种方式不是我们平常的书写习惯,所以我们想到在类外定义,然后为了 这个 "<<" 重载函数能访问到类当中的成员,我们就可以把这个重载运算符的函数,声明为有元函数,即代表着,这个函数是这个类的朋友,那么这个函数就可以访问其中 私有的成员。

 定义在类中:

class Date
{
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	// d1 << cout; -> d1.operator<<(&d1, cout); 不符合常规调用
	// 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧
	ostream & operator<<(ostream& _cout)
	{
		_cout << _year << "-" << _month << "-" << _day << endl;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};

 定义在 类外:

class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, Date& d);
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}

【注意】:

  •  有元函数可以访问 私有的 受保护的成员,但是不能访问成员函数。
  • 有元函数不能用const 修饰, 因为它不能在类当中定义,没有 this 指针。
  • 有元函数可以在类当中的任何地方进行声明,不受类访问限定符的限制
  • 一个函数可以和多个类进行有元
  • 有元函数的调用和普通函数的调用是相同的

 有元类

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

 【注意】:

  •  有元是单向的,比如 D1类是 D2类的有元类,那么D1类就可以访问其中的成员函数等等,但是 D2就不能访问 D1 当中的成员函数。
  • 有元不能传递,比如 C 是 B 的有元,B 是 A的有元,但是 C 不是 A的有元。
  • 有元关系不能继承。
class A
{
	friend class B;
public:
	int Get_a()
	{
		return _a;
	}
protected:
	int _a = 1;
};

class B
{
public:
	int GetA_a()
	{
		// 直接访问 A类当中的私有成员
		return a._a;
	}
protected:
	int _b = 0;
	A a;

};

内部类

 定义在类当中的类的就是这个类的内部类,这个内部类,就是一个类,通过外部的 类是没办法访问内部类中的私有的成员的,也就是说内部类不属于外部类。

 我们先来计算一下一个 下列代码当中 A类的 大小:

 我们发现是4,这个4只计算了 _a 这个成员的大小,因为 A 当中的 _b  是静态的,我们说类当中只是的成员只是声明,而定义是在 对象当中,但是静态的成员不存储在对象当中,而是在 全局当中进行 定义。那么 类A 当中的 类B 也是如此,A 中没有 B对象,所以不计算 B 的大小。如果我们在 A的成员中 定义了 B 这个对象那么大小就是 8 了:

 像上述的 B 类 定义的在 A类的 外面,和 B类 定义在 A类当中的 区别就是,前者是在全局域当中定义,后者是在 A这个类域当中定义。

形象的说,如果 类是图纸,对象是对应建造的房子,那么像上述 B是A的内部类,我们创建A类的对象当中是没有B类的空间的,也此时就是在 A的房子中放了 B的 图纸。

那么既然是在 A的类域当中定义,那么这个 B类就可以访问A类当中的私有成员,也就是内部类就是外部类的友元类。但是外部类不是内部类的友元。

class A
{
public:

	class B
	{
	public:
		int GetA_a()
		{
			return a._a;
		}

	protected:
		int _b = 0;
		A a;
	};

protected:
	int _a = 1;
	static int _b;
	B b;
};
  • 1. 内部类可以定义在外部类的public、protected、private都是可以的。
  • 2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  • 3. sizeof(外部类)=外部类,和内部类没有任何关系
     

 只有是 定义出来的才受到 访问限定符的限制,向上述只是声明,所以是不会限制的。

 匿名对象

class A
{
public:
	int Get_a()
	{
		return _a;
	}
protected:
	int _a;
};

int main()
{
	// 匿名创建对象
	A();
}

如上述就是使用 匿名对象的创建一个对象。

使用匿名对象来调用成员函数:

int main()
{
	// 匿名创建对象
	A().Get_a();
}

我们可以使用这样的方式来 一次性调用 类当中的成员函数,如果是多次,那么我们就得使用 普通的创建对象的方式来 调用成员函数了。

之所以这样说,是因为匿名对象是 即用即销毁

有名对象的生命周期是在 函数局部域,匿名函数的生命周期是在当前行

 【注意】:

我们不能这样去调用成员函数:

A::Get_a();

这样调用的前提是 这个函数是静态的,如果这样调用这个函数就必须没有 this 指针。而成员函数是必须有 this 指针的。

匿名对象和 临时对象一样,具有常性,所以,我们不能创建 匿名对象的普通引用:

 所以要加一个 const 才能编译通过:

	const A& pa = A();

而且当我们使用 const 引用这个 匿名对象之后,这个匿名对象的 生命周期会被延长,延长的时间是 引用的生命周期。

 如下面这个例子:

class A
{
public:
	A()
	{
		cout << "A()" << endl;
	}

	~A()
	{
		cout << "~A()" << endl;
	}

	int Get_a()
	{
		return _a;
	}
protected:
	int _a;
};

class AAAAAAAAA
{
public:
	AAAAAAAAA()
	{
		cout << "AAAAAAAAA()" << endl;
	}

	~AAAAAAAAA()
	{
		cout << "~AAAAAAAAA()" << endl;
	}
};

int main()
{

	const A& pa = A();

	AAAAAAAAA aaaaaaaa;
}

我们把 两个类的 构造函数 和 析构函数 只要已调用就打印一下,表明调用了这个函数,我们来看输出结果:

 我们发现, A() 这个匿名函数是在 AAAAAAAAA()这个对象销毁之后才销毁的。

 匿名函数的使用场景:

void Print_string(const string& st)
{
	cout << st << endl;
}

int main()
{
	string str("11111");  // 1
	Print_string(str);    // 2

	return 0;
}

上述的 代码 1 和  2 可以 使用匿名对象 写成一行代码:

Print_string(string("11111"));

此时就使用了 匿名对象 进行传参。

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

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

相关文章

程序员开发Linux常用命令

本文对程序开发过程中常用的Linux命令进行总结&#xff0c;随时进行补充&#xff0c;属于科普篇&#xff0c;希望对大家有所帮助 file命令 该命令用于查看文件的基本信息&#xff0c;比如编码格式&#xff0c;文件类型等信息&#xff0c;对于可执行程序或者动态链接库文件&am…

【Python入门知识】类和对象,要想学的好基础得打好

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! Python 类/对象 Python 是一种面向对象的编程语言。 Python 中的几乎所有东西都是对象&#xff0c;拥有属性和方法。 类&#xff08;Class&#xff09;类似对象构造函数&#xff0c;或者是用于创建对象的“蓝图”。 创建…

Java 判空的常见方法

一、 对象判空 if (obj ! null) {// 进行对象非空判断 }Object obj null; // 或者 obj new Object(); if (obj null) {// 对象为空 }另外&#xff0c;Guava 库还提供了一个更方便的方法&#xff0c;使用方式如下&#xff1a; import com.google.common.base.Objects;if (O…

SOLIDWORKS 30个实用小技巧

很多人在学习SolidWorks时&#xff0c;会有很多疑问&#xff0c;都不知道如何解答&#xff0c;所以走了很多弯路。今天&#xff0c;我们就来讲讲在学习SolidWorks中的那些小技巧吧&#xff01; 1、SOLIDWORKS技巧之按“空格键&#xff1a;”弹出快捷菜单双击某一视图&#xff0…

webSocket介绍及项目实战【在线聊天系统】

文章目录 一&#xff1a;消息推送常用方式介绍1.1 轮询&#xff1a;浏览器以指定的时间间隔向服务器发出HTTP请求&#xff0c;服务器实时返回数据给浏览器1.2 长轮询&#xff1a;浏览器发出ajax请求&#xff0c;服务器端接收到请求后&#xff0c;会阻塞请求直到有数据或者超时才…

学习之-Spring Cache缓存框架应用本地缓存

此文章用于个人学习记录&#xff0c;原文地址&#xff1a;https://zhuanlan.zhihu.com/p/452315531 如果想了解springCache与redis的交互请看其他文章 缓存是web项目不可或缺的一部分&#xff0c;通过缓存能够降低服务器数据库压力&#xff0c;提高服务器的稳定性及响应速度。…

Rust + 嵌入式:强力开发组合

Rust 的由来 Rust 编程语言的灵感诞生于一次意外。2006年&#xff0c;当 Graydon Hoare 回到位于温哥华的公寓时&#xff0c;发现电梯又因为软件崩溃出了故障。住在 21 楼的他无奈爬楼时&#xff0c;不禁心想&#xff0c;“我们搞计算机的&#xff0c;怎么连个能正常运行的电梯…

接口测试要会什么技能?测试老鸟总结,从接口到接口自动化全面盲扫...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Python自动化测试&…

YOLOv5改进:引入DenseNet思想打造密集连接模块,彻底提升目标检测性能

目录 一、密集连接模块的介绍1、密集连接的概念2、密集连接与残差连接的对比3、DenseNet的结构 二、 YOLOv5中引入密集连接模块的原因1、密集连接模块对于目标检测的优势2、密集连接模块对目标检测性能的影响 三、 YOLOv5中密集连接模块的具体实现1、使用DenseNet的基本单元Den…

怎样才能尽快从开发岗转到产品经理岗位?

越来越多的开发同学随着工作年限的增长都会产生类似的想法。 当然&#xff0c;背后的原因也是多种多样&#xff0c;像薪资上的限制、行业前景的担忧等等&#xff0c;很多同学则踌躇在原地不敢转产品岗位&#xff0c;而有的同学则是通过各种不同方式顺利转岗到产品经理岗位&…

树莓派从源码构建安装Git最新版

1、查看Git版本 首先我们通过SSH客户端连接树莓派,在树莓派中通过查看 Git 版本信息&#xff0c;我们只能看到最高版本显示为 2.30.2&#xff0c;并且通过apt安装也无法将Git更新到最新版。 git --version sudo apt upgrade git那么我们只能通过从源代码来构建安装Git了&…

单片机复习题第二章

1.在AT89S52单片机中&#xff0c;如果采用6MHz晶振&#xff0c;一个机器周期为2us。 时钟周期的振荡频率为fosc&#xff0c;则时钟周期T1/fosc。 一个机器周期包括12个时钟周期。 2.内部RAM中&#xff0c;位地址为40H&#xff0c;88H的位&#xff0c;该位所在字节的字节地址分别…

scrollIntoView 的使用

描述 将调用此方法的元素滚动到浏览器窗口的可见区域。 scrollIntoView 官方文档 用法 element.scrollIntoView() 用法同 element.scrollIntoView(true) element.scrollIntoView(alignToTop) true 表示 element 元素顶部与可见区域的顶部对齐&#xff0c;默认值false 表示 el…

基于html+css的图展示56

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

Packet Tracer - 第 2 层 VLAN 安全

Packet Tracer - 第 2 层 VLAN 安全 目标 在 SW-1 和 SW-2 之间连接新的冗余链路。 启用中继&#xff0c;并在 SW-1 和 SW-2 之间的新中继链路上 配置安全。 创建新的管理 VLAN (VLAN 20) 并将管理 PC 连接到 该 VLAN。 实施 ACL 以阻…

Java学习-GUI编程-JFrame窗口

Java学习-GUI编程-JFrame窗口 public class TestJFrame {public void init(){JFrame jf new JFrame("这是一个JFream窗口");jf.setVisible(true);jf.setBounds(100,100,400,400);JLabel label new JLabel("这是一个label");label.setHorizontalAlignmen…

Linux之shell函数和正则表达式(八)

1、函数 1、函数概述 shell 中允许将一组命令集合成语句形成一段可用代码&#xff0c;这些代码块称为 shell 函数&#xff0c;给这段代码起个名字称为函数名&#xff0c;后续可以直接调用该段代码的功能 2、定义函数 函数名() {函数体&#xff08;一堆命令的集合&#xff0…

程序员的下一个风口

面对近一年的裁员潮&#xff0c;以及 GPT 出现带来的 AI 颠覆潮流&#xff0c;各种话题出现&#xff1a;「前端已死」、「后端已死」、「Copy/Paste 程序员将被 AI 取代」。程序员行业是否还有发展空间&#xff1f; 这一两年的就业机会是因为经济衰落周期内造成的&#xff0c;不…

pmp备考有哪些适合新手的学习资料?

在备考中&#xff0c;无论你是胸有成竹的学霸还是忐忑不安的学渣&#xff0c;强烈建议你是时候展现真正的技术了——临阵磨枪不快也光&#xff01;你准备好了吗&#xff1f;前方为大家准备了高能备考技巧和干货资料包&#xff0c;拿起热水杯&#xff0c;准备走起。 1.PMBOK知识…

怎样使用Web自动化测试减少手动劳动?以百度网站为例

从入门到精通&#xff01;企业级接口自动化测试实战&#xff0c;详细教学&#xff01;&#xff08;自学必备视频&#xff09; 目录 摘要 步骤1&#xff1a;安装和配置Selenium 步骤2&#xff1a;启动浏览器并访问百度网站 步骤3&#xff1a;关闭浏览器 总结 摘要 本指南将…