当前位置: 首页 > news >正文

C++类与对象(7)—友元、内部类、匿名对象、拷贝对象时编译器优化

目录

一、友元

1、定义 

2、友元函数

3、友元类

二、内部类

1、定义

2、特性:

三、匿名对象

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

1、传值&传引用返回优化对比

2、匿名对象作为函数返回对象

3、接收返回值方式对比

总结:


一、友元

1、定义 

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
  • 友元分为:友元函数和友元类

2、友元函数

问题:现在尝试去重载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;
};

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

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加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;
}
  • 友元函数可访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用与普通函数的调用原理相同

3、友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
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){// 直接访问时间类私有的成员变量_time._hour = hour;_time._minute = minute;_time._second = second;}private:int _year;int _month;int _day;Time _time;
}
  • 友元关系是单向的,不具有交换性。 比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
  • 友元关系不能传递。如果C是B的友元, B是A的友元,则不能说明C时A的友元。
  • 友元关系不能继承,在继承位置再给大家详细介绍。

二、内部类

1、定义

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类
  • 内部类是一个独立的类, 它不属于外部类,更不能通过外部类的对象去访问内部类的成员。
  • 外部类对内部类没有任何优越的访问权限。
注意:内部类就是外类部的友元类,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

2、特性:

  • 内部类可以定义在外部类的public、protected、private都是可以的。
  • 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  • sizeof(外部类)=外部类,和内部类没有任何关系。

我们先来看一下 ”sizeof(外部类)=外部类,和内部类没有任何关系“ 在代码中怎么体现的。

class A{
private:int h;
public:class B{private:int b;};
};int main()
{A aa;cout << sizeof(aa) << endl;return 0;
}

输出结果显示,类A的对象对象只有一个int成员的大小。

在调试中也可以看到类对象aa只有一个成员变量h。

 

内部类B跟A是独立,只是受A的类域限制。

可以通过下面代码访问到B类

A::B bb;

如果B类的作用域变为私有,则不能访问到。

B天生就是A的友元。

class A{
private:int h = 0;static int k;
public:class B{public:void Print(const A& a){cout << k << endl;// >> OKcout << a.h << endl;// >> OK}};
};
int A::k = 1;int main()
{A aa;A::B bb;bb.Print(aa);return 0;
}

通过B类成功访问A类的静态成员变量k和整型成员变量h。

这时我们就可以对使用static成员的这道题使用内部类进行修改。 

求1+2+3+...+n_牛客题霸_牛客网 (nowcoder.com)

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

三、匿名对象

class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};
class Solution {
public:int Sum_Solution(int n) {//...return n;}
};
int main()
{A aa1;// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义//A aa1();// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,// 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数A();A aa2(2);// 匿名对象在这样场景下就很好用。Solution().Sum_Solution(10);return 0;
}

这样定义类对象可以吗?

A aa1();
  • 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义。

但是我们可以这么定义匿名对象,匿名对象的特点不用取名字。

A();

但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数

匿名对象在这样场景下就很好用。

Solution().Sum_Solution(10);

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

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还是非常有用的。

1、传值&传引用返回优化对比

class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a = aa._a;}return *this;}~A(){cout << "~A()" << endl;}
private:int _a;
};void func1(A aa)
{}void func2(const A& aa)
{}int main()
{A aa1 = 1; // 构造+拷贝构造 -》 优化为直接构造func1(aa1); // 无优化,不能跨表达式优化func1(2); // 构造+拷贝构造 -》 优化为直接构造func1(A(3)); // 构造+拷贝构造 -》 优化为直接构造cout << "----------------------------------" << endl;func2(aa1);  // 无优化func2(2);    // 无优化func2(A(3)); // 无优化return 0;
}

我们看一下main函数中的代码:

  • A aa1 = 1; 这里首先调用构造函数创建一个临时对象,然后调用拷贝构造函数将临时对象的内容复制到aa1。但是,编译器通常会进行优化,直接调用构造函数创建aa1,避免了不必要的拷贝构造。
  • func1(aa1); 这里调用函数func1,参数是aa1的拷贝,所以会调用拷贝构造函数。这个过程没有优化。函数func1会调用析构函数清理临时变量aa。
  • func1(2); 和 func1(A(3)); 这两行代码都是先构造一个临时对象,然后调用拷贝构造函数将临时对象的内容复制到函数参数。但是,编译器会进行优化,直接将临时对象作为函数参数,避免了不必要的拷贝构造。

然后是func2的调用:

func2(aa1); func2(2); 和 func2(A(3)); 这三行代码都是将一个对象的引用作为函数参数,所以不需要调用拷贝构造函数,也就没有优化的空间。

  •  func2(aa1)引用传值,不需要构造和析构。

  • func2(2)构造一个临时对象,然后拷贝构造给aa。

  • func2(A(3))中 A(3) 创建了一个临时对象,调用了构造函数 A(int a = 0),并输出 "A(int a)"。

    • 这是因为在函数调用 func2(A(3)); 中,临时对象被创建,即 A(3)const A& aa 表示将这个临时对象通过常引用传递给 func2 函数。在这里,没有发生拷贝构造,因为是通过引用传递的。

    • 所以在 func2 函数内部,没有额外的构造或拷贝构造的调用。当 func2 函数执行完毕,临时对象开始析构。这时调用了析构函数 ~A(),并输出 "~A()"。这是因为在函数调用结束后,局部变量(包括通过临时对象构造的 aa)会被销毁。

    • 最后,整个程序执行结束,全局的 A(3) 对象也会被销毁,调用析构函数 ~A()。因此,总共有两次析构调用。一次是在 func2 函数内部的临时对象销毁,另一次是全局的 A(3) 对象销毁。

2、匿名对象作为函数返回对象

class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a = aa._a;}return *this;}~A(){cout << "~A()" << endl;}
private:int _a;
};A func3()
{A aa;return aa;
}
A func4()
{return A();//匿名对象
}int main()
{func3();// 构造+拷贝构造A aa1 = func3();//构造+两个拷贝构造>>>优化为构造+一个拷贝构造func4(); // 构造+拷贝构造 -- 优化为构造A aa3 = func4(); // 构造+拷贝构造+拷贝构造  -- 优化为构造return 0;
}

通过对比,可以发现使用匿名对象在func4()中的好处。 

在函数 func4() 中,return A(); 创建了一个匿名对象,并且该匿名对象直接作为函数的返回值。这样,调用 func4() 将得到这个匿名对象的拷贝,而不需要额外的临时对象。因此,在 func4() 的调用中,可以直接构造并返回这个匿名对象,避免了多余的对象的创建和拷贝构造。

3、接收返回值方式对比

A func3()
{A aa;return aa;
}int main()
{A aa1 = func3(); // 拷贝构造+拷贝构造  -- 优化为一个拷贝构造cout << "****" << endl;A aa2;aa2 = func3();  // 声明和定义不在一行,不能优化return 0;
}

总结:

对象返回:

  • 接收返回值对象,尽量拷贝构造方式接收,不要赋值接收。
  • 函数中返回对象时,尽量返回匿名对象。

函数传参:

  • 尽量使用const &传参。

相关文章:

C++类与对象(7)—友元、内部类、匿名对象、拷贝对象时编译器优化

目录 一、友元 1、定义 2、友元函数 3、友元类 二、内部类 1、定义 2、特性&#xff1a; 三、匿名对象 四、拷贝对象时的一些编译器优化 1、传值&传引用返回优化对比 2、匿名对象作为函数返回对象 3、接收返回值方式对比 总结&#xff1a; 一、友元 1、定义…...

Django回顾2

目录 一.HTTP 1.URL介绍 2.格式&#xff1a; 3.补充&#xff1a; 二.web框架 1.什么是框架 2.什么是web框架 3.wsgi协议 基于wsgi协议的web服务器&#xff1a; 4.协议是怎么规定的 三.Django 1.MVC与MTV模型&#xff08;所有框架其实都遵循MVC架构&#xff09; 2.…...

<JavaDS> 二叉树遍历各种遍历方式的代码实现 -- 前序、中序、后序、层序遍历

目录 有以下二叉树&#xff1a; 一、递归 1.1 前序遍历-递归 1.2 中序遍历-递归 1.3 后序遍历-递归 二、递归--使用链表 2.1 前序遍历-递归-返回链表 2.2 中序遍历-递归-返回链表 2.3 后序遍历-递归-返回链表 三、迭代--使用栈 3.1 前序遍历-迭代-使用栈 3.2 中序遍…...

如何控制Spring工厂创建对象的次数?详解Spring对象的声明周期!

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…...

计算机杂谈系列精讲100篇-【计算机应用】PyTorch部署及分布式训练

目录 C平台PyTorch模型部署流程 1.模型转换 1. 不支持的操作 2. 指定数据类型 2.保存序列化模型 3.C load训练好的模型 4. 执行Script Module PyTorch分布式训练 分布式并行训练概述 Pytorch分布式数据并行 手把手渐进式实战 A. 单机单卡 B. 单机多卡DP C. 多机多卡DDP D. L…...

Opencv-C++笔记 (19) : 分水岭图像分割

文章目录 一、基于距离变换与分水岭的图像分割1、图像分割2、距离和变换与分水岭距离变换常见算法有两种分水岭变换常见的算法 3、距离变换API函数接口4、watershed 分水岭函数API接口步骤 5、代码 一、基于距离变换与分水岭的图像分割 1、图像分割 图像分割(Image Segmentat…...

Linux以nohup方式运行jar包

1、在需要运行的jar包同级目录下建立启动脚本文件&#xff1a; 文件内容&#xff1a; #! /bin/bash #注意&#xff1a;必须有&让其后台执行&#xff0c;否则没有pid生成 jar包路径为绝对路径 nohup java -jar /usr/local/testDemo/jdkDemo-0.0.1-SNAPSHOT.jar >/us…...

【c++|SDL】开始使用之---demo

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 SDL 记录 1. hello word #include<SDL2/SDL.h>SDL_Window* g_pWindow 0; SDL_Renderer* g_pRenderer 0;int main(int argc, char* args[]) {//…...

leetcode:有效的括号

题目描述 题目链接&#xff1a;20. 有效的括号 - 力扣&#xff08;LeetCode&#xff09; 题目分析 题目给了我们三种括号&#xff1a;&#xff08;&#xff09;、{ }、[ ] 这里的匹配包括&#xff1a;顺序匹配和数量匹配 最优的思路就是用栈来解决&#xff1a; 括号依次入栈…...

使用STM32微控制器实现光电传感器的接口和数据处理

光电传感器在许多领域中被广泛应用&#xff0c;例如工业自动化、智能家居等。本文将介绍如何使用STM32微控制器实现光电传感器的接口和数据处理的方案&#xff0c;包括硬件设计、引脚配置、数据采集、滤波和阈值判断等关键步骤&#xff0c;并给出相应的代码示例。 一、引言 光…...

ELK企业级日志分析平台——kibana数据可视化

部署 新建虚拟机server5&#xff0c;部署kibana [rootelk5 ~]# rpm -ivh kibana-7.6.1-x86_64.rpm [rootelk5 ~]# cd /etc/kibana/[rootelk5 kibana]# vim kibana.ymlserver.host: "0.0.0.0"elasticsearch.hosts: ["http://192.168.56.11:9200"]i18n.local…...

Shell条件变量练习

1.算数运算命令有哪几种&#xff1f; (1) "(( ))"用于整数运算的常用运算符&#xff0c;效率很高 [rootshell scripts]# echo $((24*5**2/8)) #(( ))2452814 14 (2) "$[ ] "用于整数运算 [rootshell scripts]# echo $[24*5**2/8] #[ ]也可以运…...

【PHP】MySQL简介与MySQLi函数(含PHP与MySQL交互)

文章目录 一、MySQL简介二、MySQLi函数1. 开启mysqli扩展&#xff1a;2. PHP MySQLi扩展的常用函数 三、PHP与MySQL交互0. 准备1. 创建连接&#xff08;mysqli_connect() &#xff09;连接mysql语法 2. 选择数据库&#xff08;mysqli_select_db()&#xff09;3. 在php中操作数据…...

vscode在Windows上安装插件提示错误xhr failed

问题描述&#xff1a; 在Windows下&#xff0c;在vscode里搜索扩展时发现无法搜索&#xff0c;报如下错&#xff1a;”Error while fetching extensions. XHR failed“。 问题定位&#xff1a; 在vscode界面下键入ctrlshiftp&#xff0c; 然后输入&#xff1a;Developer: T…...

SHAP(一):具有 Shapley 值的可解释 AI 简介

SHAP&#xff08;一&#xff09;&#xff1a;具有 Shapley 值的可解释 AI 简介 这是用 Shapley 值解释机器学习模型的介绍。 沙普利值是合作博弈论中广泛使用的方法&#xff0c;具有理想的特性。 本教程旨在帮助您深入了解如何计算和解释基于 Shapley 的机器学习模型解释。 我…...

C++数据结构:图

目录 一. 图的基本概念 二. 图的存储结构 2.1 邻接矩阵 2.2 邻接表 三. 图的遍历 3.1 广度优先遍历 3.2 深度优先遍历 四. 最小生成树 4.1 最小生成树获取策略 4.2 Kruskal算法 4.3 Prim算法 五. 最短路径问题 5.1 Dijkstra算法 5.2 Bellman-Ford算法 5.3 Floyd-…...

「C++」红黑树的插入(手撕红黑树系列)

&#x1f4bb;文章目录 &#x1f4c4;前言红黑树概念红黑树的结构红黑树节点的定义红黑树的定义红黑树的调整 红黑树的迭代器迭代器的声明operator( )opeartor--( ) 完整代码 &#x1f4d3;总结 &#x1f4c4;前言 作为一名程序员相信你一定有所听闻红黑树的大名&#xff0c;像…...

2023年生肖在不同时间段的运势预测

随着信息技术的飞速发展&#xff0c;API已经成为了数据获取和交互的重要途径。很多网站和APP都在运用API来获取数据。今天我们来介绍一个十分有趣的API——《十二生肖运势预测API》&#xff0c;通过这个API&#xff0c;我们可以获取到每个生肖在不同时间段的运势预测&#xff0…...

ERRO报错

无法下载nginx 如下解决&#xff1a; 查看是否有epel 源 安装epel源 安装第三方 yum -y install epel-release.noarch NGINX端口被占用 解决&#xff1a; 编译安装的NGINX配置文件在/usr/local/ngin/conf 修改端口...

shiyan

import javax.xml.transform.Result; import java.util.Arrays; public class ParseText {//需要统计的字符串为private String text"Abstract-This paper presents an overview";private Result[] res;private int count;public ParseText(){resnew Result[100];cou…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...