【C++】-类和对象完结(内部类、匿名对象以及编译器的优化的讲解)(下)

news2024/11/18 9:30:47

💖作者:小树苗渴望变成参天大树
❤️‍🩹作者宣言:认真写好每一篇博客
💨作者gitee:gitee
💞作者专栏:C语言,数据结构初阶,Linux,C++

在这里插入图片描述如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!

文章目录

  • 前言
  • 一、友元(friend)
    • 1.1友元函数
    • 1.2友元类
  • 二、内部类
  • 三、匿名对象
  • 四、拷贝对象时的一些编译器优化
  • 五、总结


前言

今天我们再来重新认识一下友元,友元就像朋友,使用了友元,你就可以随便来我家里做客,访问类里面私有的东西,相当于开了挂,我们在来回顾一下


一、友元(friend)

1.1友元函数

在之前尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。这是友元函数

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;
};

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。

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; 
}
istream& operator>>(istream& _cin, Date& d)
{
 _cin >> d._year;
 _cin >> d._month;
 _cin >> d._day;
 return _cin;
}
int main()
{
 Date d;
 cin >> d;
 cout << d << endl;
 return 0;
}

说明:

  1. 友元函数可访问类的私有和保护成员,但不是类的成员函数
  2. 友元函数不能用const修饰
  3. 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  4. 一个函数可以是多个类的友元函数
  5. 友元函数的调用与普通函数的调用原理相同

1.2友元类

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

  1. 友元关系是单向的,不具有交换性。
    比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
  2. 友元关系不能传递
    如果C是B的友元, B是A的友元,则不能说明C时A的友元。
    友元关系不能继承,在继承位置再给大家详细介绍。
class Time
{
   friend class Date;   // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类
中的私有成员变量
public:
 Time(int hour = 0, int minute = 0, int second = 0)
 : _hour(hour)
 , _minute(minute)
 , _second(second)
 {}
   
private:
   int _hour;
   int _minute;
   int _second;
};
class Date
{
public:
   Date(int year = 1900, int month = 1, int day = 1)
       : _year(year)
       , _month(month)
       , _day(day)
   {}
   
   void SetTimeOfDate(int hour, int minute, int second)
   {
       // 直接访问时间类私有的成员变量
       _t._hour = hour;
       _t._minute = minute;
       _t._second = second;
   }
   
private:
   int _year;
   int _month;
   int _day;

上述在日期类中可以访问时间类的私有成员,反过来就不行,对于友元我们知道怎么用就行了,以后用的也少,接下来讲解其他知识点

二、内部类

在这C++里面用的也比较少,在Java里面用的也比较多,但是我也简单的介绍一下

内部类简单来说就是在类里面又定义一个类

class A
{
private:
 static int k;
 int h;
public:
 class B 
 {
 private:
 int m;
 int n;
 public:
 void foo(const A& a)
 {
 cout << k << endl;//OK
 cout << a.h << endl;//OK
 }
 };
};
int A::k = 1;
int main()
{
A a;
   cout<<sizeof(a)<<endl;
    
    return 0;
}

在这里插入图片描述

我们只计算了A类中h的大小,k在静态区,不属于对象
在这里插入图片描述
我们看到只有创建了内部类对象,才算外部类的大小,==我们把内部的声明当成图纸,创建了对象才当成外部类的成员变量,这样就能很好的理解了。

那我们怎么只使用B类的内容呢??前提是共有的,私有的就访问不了

A::B b;

这样就可以了

内部类天生就是外部类的友元函数:

class A
{
private:
	static int k;
	int h;
public:
	class B // B天生就是A的友元
	{
	private:
		int m;
		int n;
	public:
		void foo(const A& a)
		{
			cout << k << endl;//OK
			cout << a.h << endl;//OK
		}
	};
	B b;
};

我们在foo函数访问到了外部类中私有的成员变量,反过来就不行了

通过内部类我们来一下上篇博客的那题:

class Solution{
private:
static int _i;
static int _ret;
public:
class Sum
{
   public:
    Sum()
    {
       _ret+=_i;
       _i++;
    }
};
    int Sum_Solution(int n) {
        Sum a[n];
        return _ret;
    }
};
int Solution::_i=1;
int Solution::_ret=0;

不需要再写一个函数来将结果返回出来,直接进行访问就行了

三、匿名对象

匿名对象再这里我就给大家讲讲用法和一些注意事项,现在用到的也不多,以后再单例的时候会用到,那个时候再细讲一下

匿名对象顾名思义就是没有名字的对象
我们来具体看看:

class A
{
public:
 A(int a = 0)
 :_a(a)
 {
 cout << "A(int a)" << endl;
 }
 ~A()
 {
 cout << "~A()" << endl;
 }
private:
 int _a;
};

A a;
A a1(1);//这都是有名的对象

我们来具体怎么使用

A ;//对于不传参的匿名对象,我们不可能写成这样,我们只有加一个括号来区分
A();


A (1);传参的匿名对象

在这里插入图片描述
匿名对象的声明周期就再当前行,即用即销毁,匿名对象出现就把他当成只有一个使用机会的对象,对象怎么去调用函数的,他就怎么去调用。

我们再来看下面的代码:
在这里插入图片描述
我们看到这样就不行了,原因是匿名对象和临时对象都有常性,加一个const就行了

在这里插入图片描述
我们此时来看这个匿名对象的生命周期是多少:
在这里插入图片描述

没有即用即销毁,可以这么说引用延长了匿名对象的生命周期,可以这么简单理解,之前创建了匿名对象没人使用,就即用即销毁,现在有人用了,就延长销毁时间了

这个以后再String类的时候会用到。以后再详细介绍,这个大家会使用就行了。

四、拷贝对象时的一些编译器优化

再介绍explicit关键字的时候,这个关键字是防止隐式类型转换,隐式类型转换是将一个整型先构造一个临时变量,再拷贝构造。大家要是忘记了,可以先看看这篇博客explicit

Date d1;//最多可以传一个参数的类
d1=2023;//构造+拷贝构造

这是一条语句有这种重复的操作,编译器会进行优化变成一个操作,只会进行构造。如果这两个操作不在一条语句就不会有这种优化,接下来会通过几个例子给大家展示:
在这里插入图片描述
第一个因为不在一个语句,就没有办法进行优化。第二个匿名对象也会调用构造函数,传参会调用拷贝构造,编译器进行了优化,直接构造。

在这里插入图片描述
优化不止会发现再构造和拷贝构造中,不止会发生再拷贝构造和拷贝构造之间

我写的不一定正确,大家有问题可以提出来,写这个的目的就是想让大家知道,编译器对于重复的操作会有优化,大家了解就好了,也方便大家理解怎么调用析构函数和构造函数,不然大家去打印是调用了几次构造,不知道编译器会优化的话,总会发现少一个构造,总是对不上

大家可以去测试一下这些函数:

void f1(A aa)
{}
A f2()
{
 A aa;
 return aa;
}
int main()
{
 // 传值传参
 A aa1;
 f1(aa1);
 cout << endl;
 // 传值返回
 f2();
 cout << endl;
 // 隐式类型,连续构造+拷贝构造->优化为直接构造
 f1(1);
 // 一个表达式中,连续构造+拷贝构造->优化为一个构造
 f1(A(2));
 cout << endl;
 // 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造
 A aa2 = f2();
 cout << endl;
 // 一个表达式中,连续拷贝构造+赋值重载->无法优化
 aa1 = f2();
 cout << endl;
 return 0;
}

五、总结

今天学的东西不难,大家看到知道是什么并且有一定的了解,会简单的使用就行了,类和对象的知识点和语法几乎就已经讲解完了,大家掌握的怎么样呢,接下来我将讲解C++中的动态内存管理知识,有了C语言的支持,C++这一块不是特别难理解,大家一定要先把前面的知识弄懂才能额更好的理解后面讲的内容,我们下篇再见了

在这里插入图片描述

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

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

相关文章

m1安装svn

背景&#xff1a;电脑是mac m2&#xff0c;好多软件都不太兼容&#xff0c;安装软件成了一个问题&#xff0c;想着装一个SVN&#xff0c;跟大家一起协同开发&#xff0c;这下可麻烦死了&#xff0c;&#x1f604;&#xff0c;终于弄明白用brew命令了&#xff0c;然后就用brew命…

Metasploitable2靶机渗透学习

目录 一、介绍 二、环境 三、渗透攻击 1.前期渗透 1.1主机发现 1.2.端口扫描 1.3.测试漏洞 2.弱密码漏洞 2.1系统弱密码登录&#xff08;telnet &#xff1a;23端口&#xff09; 2.2 MySQL弱密码登录&#xff08;端口&#xff1a;3306&#xff09; 2.3 PostgreSQL弱…

K8s全套快速入门

K8s快速入门 1 介绍 google开源的容器化管理工具机器数量十几台、上百台时&#xff0c;就可以考虑使用k8s高可用、自动容灾恢复、灰度更新、一键回滚历史版本、方便伸缩扩展等 k8s集群架构&#xff1a; 通常&#xff1a;一主多从 master&#xff1a;主节点&#xff0c;控制平台…

LeetCode 栈和队列OJ题目分享

目录 有效的括号&#xff08;括号匹配&#xff09;用栈实现队列用队列实现栈设计循环队列 有效的括号&#xff08;括号匹配&#xff09; 链接: link 题目描述&#xff1a; 题目思路&#xff1a; 1、如果是左括号“&#xff08; { [ ”就入栈 2、如果是右括号“&#xff09; }…

Redis--弱口令未授权访问漏洞

Redis--弱口令未授权访问漏洞 一、漏洞简介二、危险等级三、漏洞影响四、入侵事件五、漏洞复现--Redis CrackIT入侵事件5.1、以root启动的redis&#xff0c;可以远程登入到redis console--------A主机5.2、生成公钥5.3、执行: redis-cli flushall 清空redis(非常暴力&#xff0…

2023年春秋杯网络安全联赛 春季赛 wp

文章目录 Cryptocheckinbackdoor WebPhpstudyEasypyezrustqqcms MISCSudohappy2forensic盲人会藏在哪里piphackwordle PWNp2048easy_LzhiFTP_CHELL Crypto checkin 第一部分求解一下pell函数得到x,y def solve_pell(N, numTry 100):a[]b[]cf continued_fraction(sqrt(N))f…

C++的priority_queue

priority_queue 1.priority_queue的介绍2.priority_queue的使用3.priority的模拟实现 1.priority_queue的介绍 优先队列是一种堆&#xff0c;默认是大根堆&#xff0c;可以通过greater的仿函数可以建立小根堆empty()&#xff1a;检测容器是否为空 size()&#xff1a;返回容器中…

【密码学复习】第七章 公钥加密体制(二)

RSA单向陷门函数及其应用 ElGamal单向陷门函数 1&#xff09;密钥生成 ① 选择一大素数p,选取Zp * 的生成元g ; ② 任选小于p的随机数x&#xff0c;计算y≡g x mod p; ③(y, g, p)为公开密钥&#xff0c; (x, g, p)为秘密密钥. 2&#xff09;加密&#xff1a;设待加密…

asp.net就业满意度问调查系统

本系统主要有会员&#xff08;调查者&#xff09;和管理员&#xff0c;他们具体的功能如下&#xff1a; 会员功能&#xff1a;注册&#xff0c;登录&#xff0c;修改个人信息&#xff0c;调查&#xff0c;查看调查结果及影响&#xff0c;留言,首先是会员注册&#xff0c;注册后…

【Web开发】Node实现Web图表功能(ECharts.js,React)

&#x1f388;&#x1f388;&#x1f388;Python实现Web图表功能系列&#xff1a;&#x1f388;&#x1f388;&#x1f388;1&#x1f388;【Web开发】Python实现Web图表功能&#xff08;D-Tale入门&#xff09;&#x1f388;2&#x1f388;【Web开发】Python实现Web图表功能&a…

整理了一份github上比较热门的ChatGPT项目,值得收藏

ChatGPT已经火了一段时间了&#xff0c;但是&#xff0c;热度依旧是各大自媒体的热榜。由于&#xff0c;国内不能直接访问ChatGPT,国内的开发者依托OpenAI的接口&#xff0c;开发出一些ChatGPT的应用。今天就整理一下github上最热门的ChatGPT项目。 lencx/ChatGPT 该项目是Cha…

java线程的状态

文章目录 1. 线程的状态2. 验证NEW、RUNNALE和TERMINATED状态3. 验证TIMED_WAITING状态4. 验证BLOCKED状态5. 验证BLOCKED状态 1. 线程的状态 线程在不同的运行时期存在不同的状态&#xff0c;状态信息存在于State枚举类中&#xff0c;如下图&#xff1a; 调用线程有关的方法是…

文心一言 VS 讯飞星火 VS chatgpt (18)-- 算法导论4.1 5题

五、使用如下思想为最大子数组问题设计一个非递归的、线性时间的算法。从数组的左边界开始&#xff0c;由左至右处理&#xff0c;记录到目前为止已经处理过的最大子数组。若已知 A[1…j]门的最大子数组&#xff0c;基于如下性质将解扩展为 A[1…j1]的最大子数组:A[1…j1]的最大…

Squid 代理服务器

Squid概述 Squid 主要提供缓存加速、应用层过滤控制的功能。 代理的工作机制 1&#xff0e;代替客户机向网站请求数据&#xff0c;从而可以隐藏用户的真实IP地址。 2&#xff0e;将获得的网页数据&#xff08;静态 Web 元素&#xff09;保存到缓存中并发送给客户机&#xff0…

Windows安装Ubuntu双系统

Windows安装Ubuntu双系统 1.下载Ubuntu 16.04&#xff0c;地址https://releases.ubuntu.com/16.04/ 2.下载Rufus&#xff0c;地址https://rufus.ie/zh/ 3.准备U盘&#xff0c;烧录系统 4.磁盘分区 5.重启&#xff0c;按住shift键 本人电脑是联想小新 Windows11系统&#xff0…

QT上位机串口+STM32单片机项目

第一个自己的上位机小项目&#xff0c;嘿嘿&#xff0c;还是有些成绩感的。 目录 1.先看QT上位机部分 1.首先写一个页面 2.mainwindow.cpp主要函数。 2.form.cpp主要函数 3.STM32部分 1.main函数 3.QT完整代码 1.shangwei.pro 2.form.h 3.mainwindow.h 4.form.cpp …

从零入门激光SLAM(十一)——如何求解SLAM问题

大家好呀&#xff0c;我是一个SLAM方向的在读博士&#xff0c;深知SLAM学习过程一路走来的坎坷&#xff0c;也十分感谢各位大佬的优质文章和源码。随着知识的越来越多&#xff0c;越来越细&#xff0c;我准备整理一个自己的激光SLAM学习笔记专栏&#xff0c;从0带大家快速上手激…

(转载)从0开始学matlab(第9天)—第一阶段总结

1.编程实例 下面的例子将向大家介绍如何用 MATLAB 解决问题。 例1 温度转换程序 问题&#xff1a; 设计一个 MATLAB 程序&#xff0c;读取一个华氏温度的输入&#xff0c;输出开尔文温度。 答案&#xff1a; 华氏温度和开尔文温度的转换关系式可在物理学课本中找到。其关系式…

HCIP-RIP双向重发布综合实验

拓扑结构&#xff1a; 要求&#xff1a; 1、两个协议间进行多点双向重发布 2、R7的环回没有宣告在OSPF协议中&#xff0c;而是在后期重发布进入的 3、解决环路&#xff0c;所有路径选择最优&#xff0c;且存在备份 4、R2的环回要在RIP中宣告&#xff0c;R3的环回要在OSPF中宣…

如何优雅的写C#,使用Visual studio

免责声明 本人接触C#,.Net一年时间&#xff0c;本文内容基于我平时对于C#语法的积累&#xff0c;如有问题请多包涵。以下内容除了C#之外&#xff0c;还有Visual studio编译器相关的内容。 在使用C#的一年多里面&#xff0c;我发现C#的语法糖真的很不错&#xff0c;Visual Stu…