深入篇【C++】类与对象:详解内部类+匿名对象+编译器对拷贝的优化

news2024/11/28 8:46:20

这里写目录标题

  • Ⅰ.内部类
    • 【特点】
      • 1.天生友元
      • 2.直接访问static成员
      • 3.访问限制符限制
      • 4.外部类的大小
  • Ⅱ.匿名对象
    • 【特点】
      • 1.一行生命域
      • 2.对象具有常性
      • 3.可强行续命
  • Ⅲ.拷贝对象时编译器的优化

Ⅰ.内部类

概念:一个类定义在另一个类内部,这个内部的类就叫做内部类。
内部类是一个独立的类,它不属于外部类,更不能通过外部的对象来访问内部类的成员。外部类和内部类理论上没有什么关系,外部类对内部类没有任何优越的访问限定。

class A
{
public:
    class B//B类写在A类里面,是内部类
    {
    public:
        void func(const A& a1)
        {
        }
    };
private: 
   static int _a;
    int x=0;
};
int A::_a = 1;

那内部类有什么特点呢?

【特点】

1.天生友元

内部类天生就是外部类的友元类,内部类可以通过对象参数来访问外部类的成员变量,但是外部类不是内部类的友元。

class A
{
public:
    class B//B为内部类,天生是A类的友元
    {
    public:
        void func(const A& a1)//友元可以通过对象参数来访问外部类的成员
        {
            cout << a1.x << endl;//没有问题
        }
    };
private: 
   static int _a;
    int x=0;
};
int A::_a = 1;

天生友元这个使用就很灵活了。
在外部类定义的成员变量,内部类可以访问,外部类也可以访问到的。
所以一般当内部类和外部类配合使用时,就将内部类的成员定义到外部类去。
这样的话,内部类可以访问,外部类也可以访问。

比如下面这段代码求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
我们可以通过定义n个sum对象,n个sum对象就会调用n次构造函数。
我们只要在构造函数里进行计算即可,不过要注意的是,计算的变量必须是静态变量才可以。
最后再写一个GetRet函数来获取最后的结果,因为在solution函数里无法直接访问sum对象中的私有结果。

class sum {
public:
    sum()
    {
        _ret += _i;
        ++_i;
    }
    static int GetRet()
    {
        return _ret;
    }
private:
    static int _i;
    static int _ret;
};
int sum::_i = 1;
int sum::_ret = 0;
class Solution {
public:
    int Sum_Solution(int n) {

        //sum a[n];//调用n次构造函数。
        return sum::GetRet();
    }
};

这个案例我们就可以使用内部类来改造。
我们将原来sum类中的成员变量写到外面的solution类中去,这样内部类可以使用,外部类也可以直接使用,这样最后就不用再写GetRet函数了,因为solution函数可以访问到_ret结果。


class Solution {
    //默认是私有的,如果想让别人访问可以写public
    class sum {
    public:
        sum()//内部类,天生是外部类的友元,可以使用外部类的成员变量,因为为静态成员,所以不需要对象就可以使用
        {
            _ret += _i;
            ++_i;
        }
    };
public:
    int Sum_Solution(int n) {

       // sum a[n];//调用n次构造函数。
        return _ret;//这里可以直接返回——ret因为它就是类的成员变量
    }
private:
    static int _i;
    static int _ret;
};
int Solution::_i = 1;
int Solution::_ret = 0;

2.直接访问static成员

注意内部类是可以直接访问外部类的static成员变量的,因为static成员不需要this指针就可以访问,所以不需要外部类的对象/类名。

class A
{
public:
    class B
    {
    public:
        void func(const A& a1)
        {
            cout << _a << endl;//内部类可以直接访问外部类的static成员
            //非常OK,不需要外部类的对象
            cout << a1.x << endl;//没毛病
        }
    };
private: 
   static int _a;
    int x=0;
};
int A::_a = 1;

3.访问限制符限制

内部类可以定义类外部类的public,protected,private,都是可以的,取决于使用者。当想被别人访问的就写在public中,不想被别人使用的就写在private中,所以又分为公有内部类和私有内部类。

为什么会受访问限定符的限制呢?因为内部类定义的是一个类型,而访问限定符就是用来限定不同类型的访问的,不同于友元类,友元是不受访问限定符限制的,那是因为写在类里面的只是声明,就是一张图纸而已。

所以当你有这样的想法时:希望这个类藏到类里面,别人访问不到时,就使用private限定。

class A
{
public://公有内部类
    class B
    {
    public:
        void func(const A& a1)
        {
            cout << _a << endl;
            cout << a1.x << endl;
        }
    };
private: 
   static int _a;
    int x=0;
};
class A
{
private://私有内部类
    class B
    {
    public:
        void func(const A& a1)
        {
            cout << _a << endl;
            cout << a1.x << endl;
        }
    };
private: 
   static int _a;
    int x=0;
};

4.外部类的大小

sizeof(外部类)=外部类,和内部类是没有任何关系的。

class A
{
public:
    class B
    {
    public:
        void func(const A& a1)
        {
            cout << _a << endl;
            cout << a1.x << endl;
        }
    };
private: 
   static int _a;
    int x=0;
};
int A::_a = 1;
int main()
{
    A a1;
    cout << sizeof(A) << endl;
}

在这里插入图片描述
为什么是4呢?
首先我们要知道静态变量是不算进去的,因为静态变量就没有存到对象里,所以它是不算A类的大小的。
然后内部类是不占空间的,因为它在类A中只是声明而已,并没有实例化,定义出对象出来,只有在A类中定义了对象B,这样才可以把B类的大小算进去。
在这里插入图片描述
在使用内部类之前记得要指定类域才可以正常使用。

Ⅱ.匿名对象

匿名对象就是没有名字的对象

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

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

private:
	int _a;
};
int main()
{
	A aa1(1);//有名对象
	//A aa2(),可不能这样写哈,这样写编译器会无法识别这是在创建对象呢还是在声明一个函数呢,当不给参数时,后面就不要给括号。
	A(2);//匿名对象
}

那匿名对象有什么用呢?有什么特性呢?

【特点】

1.一行生命域

匿名对象与有名对象的区别在于其生命周期不同。
1.有名对象—生命周期在当前函数局部域。
2.匿名对象—生命周期就只在当前行。
在这里插入图片描述
在这里插入图片描述
当我们想直接一次性的调用某个成员函数时,就可以使用匿名对象调用,当想重复调用某个成员函数时,就用有名对象。

class Solution {
public:
	int Sum_Solution(int n) {
		cout << "Sum_Solution(int n)" << endl;
	}
};
int main()
{
	
	Solution().Sum_Solution(10);//匿名对象调用成员函数,一次性的
}

2.对象具有常性

我们知道临时变量是具有常性的,而匿名对象也具有常性。
比如下面这个代码就可以验证出来。

A& a1 = A(1);

在这里插入图片描述
匿名对象具有常性,所以不可以用引用。但只要给引用前面加上const就可以了。

3.可强行续命

当我们给引用前面加上const后就可以给匿名对象引用了。而加上引用后就出现了一个神奇的现象:匿名对象的生命周期改变了。

int main()
{
   const A& a1 = A(1);//OK
}

什么叫强行续命呢?就是原来匿名的生命周期就只是当前行,进入下一行后就销毁了,但用const引用后延长了匿名对象的生命周期,生命周期就跟引用对象一样长了。
为什么呢?可以这样解释:正常的匿名对象,使用完后,后面没有人要使用了,所以使用完就销毁掉了,而用const引用的对象因为还有一个引用一直在吊着它,它如果销毁了就不合理了,所以生命周期延长跟引用对象一样。

Ⅲ.拷贝对象时编译器的优化

在传参和传值返回的过程中,一些编译器会做一些优化,减少对象的拷贝。

当在一行连续的构造时编译器就会进行优化。

class A
{
public:
	A(int a = 10)
		:_a(a)
	{
	   cout << "A(int a = 10)" << endl;
	}	
	A(const A& aa)
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}
	A& operator=(const A& aa)
	{
		if (this != &aa)
		{
			_a = aa._a;
		}
		return *this;
   }
	~A()
	{
	   cout << "~A()" << endl;
	}
private:
	int _a;
};
A Func1()
{
	A aa2;
	return aa2;
}
void Func2(A a)
{
}

int main()
{
	A aa1 = 2;//一个表达式中,构造+拷贝构造---->优化成构造
	
	Func2(5);//跟上面类似,属于隐式类型转化
	//一个表达式中,连续构造+拷贝构造--->优化为直接构造
	
	A aa3 = Func1();//一个表达式中,拷贝+拷贝-->优化成一次拷贝
	
	但对于不是同一行的连续构造,编译器是不会做出处理的。
	
	cout << "----------" << endl;
	
	A aa5;//构造函数
	aa3 = Func1();//拷贝函数+赋值运算符重载
	//编译器不会进行优化
}

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

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

相关文章

【Unity100个实用小技巧】如何修改UI上材质的Shader

☀️博客主页&#xff1a;CSDN博客主页&#x1f4a8;本文由 萌萌的小木屋 原创&#xff0c;首发于 CSDN&#x1f4a2;&#x1f525;学习专栏推荐&#xff1a;面试汇总❗️游戏框架专栏推荐&#xff1a;游戏实用框架专栏⛅️点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd;&#…

mysql基本操作1

库的基本操作 1.show variables like character_set_database 查看系统默认的字符集&#xff0c;若是指定数据库下使用该SQL&#xff0c;则查看的是该数据库对应的字符集。 2.show variables like collation_database 查看系统默认的字符集校验规则&#xff0c;指定数据库下使用…

Java 集合 - List 接口

文章目录 1.List 接口介绍2.List 接口常用 API3.ListIterator 迭代器4.ArrayList - 动态数组4.1 ArrayList 概述4.2 手撸动态数组 5.Vector - 动态数组6.LinkedList - 双向链表6.1 链表概述6.2 手撸双链表6.3 链表与动态数组的区别 7.Stack - 栈8.总结 1.List 接口介绍 在 Jav…

Linux:查看主机运行状态的一系列命令:top、df、iostat、sar

Linux&#xff1a;查看主机运行状态的一系列命令&#xff1a;top、df、iostat、sar 命令top监控系统资源&#xff1a; 使用top(回车)命令后&#xff0c;整个控制台会变成任务管理器的形式&#xff1a; 退出可以使用&#xff1a;ctrlc 或 q 第一行补充&#xff1a;表示正在执行的…

高程复习 欧几里得算法和扩展欧几里得算法考试前冲刺简约版

gcd(m,n)gcd(n,m%n) gcd欧几里得算法标准代码求最大公约数 #include <iostream>using namespace std;typedef long long LL; LL gcd(int a,int b) {if(b0)return a;return gcd(b,a%b); } int main() {LL a,b;cin>>a>>b;cout<<gcd(a,b)<<endl;re…

Linux基础:文件权限详细说明(全)

一、前提 我们要知道&#xff0c;Linux系统&#xff0c;一切皆文件的含义。 对于Linux来说&#xff0c;一切皆文件。 我们常涉及到的概念是目录和文件。 权限主要有三种&#xff1a;r(读)w(写)x(执行)。 二、正文 1、修改文件或者目录所属用户和所属组 chown [用户名[:组名…

规则网络构建

规则网络构建 文章目录 规则网络构建[toc]1 规则网络定义2 规则网络的构建3 代码实现 1 规则网络定义 常见规则网络包包括全局耦合网络、最近邻耦合网络和星型耦合网络&#xff0c;三种规则网络定义如下&#xff1a; (1)全局耦合网络&#xff1a;任意两个节点均存在连边的网络…

云原生 HTAP -- PolarDB-IMCI:A Cloud-Native HATP Database

文章目录 0 背景1 IMCI 架构1.1 架构演进的背景1.2 基本架构1.2 基本使用1.4 列索引存储 设计1.5 RW-RO 的数据同步实现1.5.1 CALS1.5.2 2P-COFFER 1.6 计算引擎实现1.7 性能 近期除了本职工作之外想要再跟进一下业界做讨论以及落地的事情&#xff0c;扩宽一下视野&#xff0c;…

算法7.从暴力递归到动态规划0

算法|7.从暴力递归到动态规划0 1.汉诺塔 题意&#xff1a;打印n层汉诺塔从最左边移动到最右边的全部过程 解题思路&#xff1a; 把字母抛掉&#xff0c;变成左中右三个盘子多个盘子能一下到吗&#xff1f;不能&#xff0c;把上边的拿走&#xff0c;最下边的才能放到指位置(…

java汉字转拼音pinyin4j-2.5.0.jar用法

要先下载哦&#xff0c; pinyin4j下载链接 可能会出现Cannot resolve symbol ‘net’&#xff0c;找到上面文件的下载路径&#xff0c;IDEA中File->Project Structure -> Modules->Dependencies import java.util.*; import net.sourceforge.pinyin4j.PinyinHelper;…

算法基础学习笔记——⑬质数\约数

✨博主&#xff1a;命运之光 ✨专栏&#xff1a;算法基础学习 目录 ✨质数 &#x1f353;&#xff08;1&#xff09;质数的判定——试除法 &#x1f353;&#xff08;2&#xff09;分解质因数——试除法 ✨约数 &#x1f353;&#xff08;1&#xff09;试除法求一个数的所…

算法基础学习笔记——⑪拓扑排序\最短路

✨博主&#xff1a;命运之光 ✨专栏&#xff1a;算法基础学习 目录 ✨拓扑排序 &#x1f353;朴素dijkstra算法&#xff1a; &#x1f353;堆优化版dijkstra : &#x1f353;Bellman-Ford算法 &#x1f353;spfa 算法&#xff08;队列优化的Bellman-Ford算法&#xff09; …

操作系统(2.8)--线程的实现

目录 线程的实现方式 1.内核支持线程(KST) 2.用户级线程(ULT) 3.组合方式 线程的实现 1.内核支持线程的实现 2.用户级线程的实现 线程的创建和终止 线程的实现方式 1.内核支持线程(KST) 内核支持线程&#xff0c;与进程相同&#xff0c;是在内核的支持下运行的&#x…

二叉树及其相关题目相关的功能的实现

前言&#xff1a;前面我们简单提及了二叉树的相关初级知识和顺序实现二叉树的相关操作详解&#xff0c;并且由完全二叉树延伸到了堆的相关知识&#xff0c;具体详见二叉树初阶和堆的详解&#xff0c;今天&#xff0c;我们展开二叉树的相关 的链式实现操作和经常考察的二叉树的相…

2023 华为 Datacom-HCIE 真题题库 07--含解析

多项选择题 1.[试题编号&#xff1a;190187] &#xff08;多选题&#xff09;如图所示的拓扑采用了VXLAN分布式网关&#xff0c;SW1上的VBDIF10配置了&#xff1a;arp-proxy local enable命令&#xff0c;则以下描述中正确的有哪些项&#xff1f; A、SW1收到PC1发往PC2的报文&…

【搭建私人图床】使用LightPicture开源搭建图片管理系统并远程访问

文章目录 1.前言2. Lightpicture网站搭建2.1. Lightpicture下载和安装2.2. Lightpicture网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 现在的手机越来越先进&#xff0c;功能也越来越多&#xff0c;而手机…

C#,码海拾贝(22)——“全选主元高斯-约当消去法“求解“线性方程组“的C#源代码,《C#数值计算算法编程》源代码升级改进版

using System; namespace Zhou.CSharp.Algorithm { /// <summary> /// 求解线性方程组的类 LEquations /// 原作 周长发 /// 改编 深度混淆 /// </summary> public static partial class LEquations { /// <summary> …

天天被开发怼?4个方法区分bug前后端归属,我再也不背锅了!

“开发都这么不友善吗&#xff1f;” 有朋友跟我说&#xff0c;刚上岗经常分不清bug是前端还是后端&#xff0c;一直需要开发帮忙重新指派&#xff0c;甚至还会被开发拿来吐槽.... 其实不是开发态度不好&#xff0c;而是对于前后端分离的应用&#xff0c;既需要进行功能测试&am…

什么是网络安全?如何让普通人简单的了解网络安全

一、介绍网络安全 可以介绍一下河南郑州的网络安全科技馆。网络安全科技馆设置个人安全、政企安全、社会安全、综合竞技四个主展区&#xff0c;帮大家普及网络安全知识。首先&#xff0c;可以从个人安全展区开始游览&#xff0c;了解我们身边的网络安全&#xff0c;原来网络安…

调幅波解调-二极管峰值包络检波器【Multisim】【高频电子线路】

目录 一、实验目的与要求 二、实验仪器 三、实验内容与测试结果 1.观测输入、输出波形&#xff0c;估算检波效率&#xff08;D1接法不同&#xff0c;分别观测&#xff09; 2.观察惰性失真波形(C1100nF&#xff0c;其他参数保持不变) 3.观测负峰切割失真(ma0.8&#xff0c…