CD27.【C++ Dev】类和对象(18)友元和内部类

目录

1.友元

友元函数

几个特点

友元类

格式

代码示例

2.内部类(了解即可)

计算有内部类的类的大小

分析

注意:内部类不能直接定义

内部类是外部类的友元类

3.练习


承接CD21.【C++ Dev】类和对象(12) 流插入运算符的重载文章

1.友元

友元函数

CD21.【C++ Dev】类和对象(12) 流插入运算符的重载文章中提到过,明确友元函数的特点:特点为:不受访问限定符限制,突破封装的限制 ,可访问类的私有和保护成员,这里介绍友元函数的几个特点

几个特点

1.友元函数是定义在类外 部的普通函数,不是类的成员函数

2.友元函数不能用const修饰

解释:const修饰一般修饰的是成员函数,显然友元函数不属于任何类,无this指针,因此不能用const修饰

3.友元函数可以在类定义的任何地方声明,不受类访问限定符限制

这个在CD21.【C++ Dev】类和对象(12) 流插入运算符的重载文章讲过了,不再赘述

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

因为友元函数不属于任何类,所以可以是多个类的友元函数

cpp 复制代码
#include <iostream>
using namespace std;
class Myclass2;//前向声明
class Myclass1
{
   friend void function(const Myclass1& obj1, const Myclass2& obj2);
private:
    int _val = 1;
};

class Myclass2
{
    friend void function(const Myclass1& obj1, const Myclass2& obj2);
private:
    int _val = 2;
};

void function(const Myclass1& obj1, const Myclass2& obj2)
{
    cout << "function(),Myclass1's val=" <<obj1._val<< endl;
    cout << "function(),Myclass2's val=" <<obj2._val<< endl;
}
int main()
{
    Myclass1 obj1;
    Myclass2 obj2;
    function(obj1,obj2);
    return 0;
}

提醒:Myclass2必须前向声明,编译器默认是向上查找,否则编译器在遇到Myclass1的友元函数的声明friend void function(const Myclass1& obj1, const Myclass2& obj2);无法正确识别Myclass2

运行结果:

5.友元函数的调用与普通函数的调用原理相同

参见上方代码理解

友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员,这是由friend的特性实现的

格式

cpp 复制代码
friend class 类的名称

代码示例

cpp 复制代码
#include <iostream>
using namespace std;
class Myclass1
{
	friend class Myclass2;//突破Myclass1的private限制
private:
	int _val = 1;

};

class Myclass2
{
public:
	void function()
	{
		cout << "Myclass1's _val:" << _obj._val << endl;
	}
private:
	Myclass1 _obj;//通过_obj去访问
};

int main()
{
	Myclass2 obj;
	obj.function();
    return 0;
}

如果删除单词friend,那么就会报错:

运行结果:

2.内部类(了解即可)

顾名思义,即一个类声明在另一个类的里面,代码框架如下:

cpp 复制代码
class Myclass1
{
public:
	//......
	class innerclass
	{
	public:
		//......
	private:
		//......
	};
private:
	//......
};

计算有内部类的类的大小

提问:obj对象的大小是多少?

cpp 复制代码
#include <iostream>
using namespace std;
class Myclass
{
public:
	void function1()
	{
		cout << "void function1()" << endl;
	}
	class innerclass
	{
	public:
		int function2()
		{
			return 0;
		}
	private:
		static int _val2;
	};
private:
	int _val1;
};

int main()
{
	Myclass obj;
    return 0;
}

分析

可以从运行结果来推测,使用以下代码:

cpp 复制代码
int main()
{
	Myclass obj;
	cout << sizeof(obj) << endl;
}

运行结果:发现大小为4字节

从结果反推:

obj里面没有自定义类型为innerclass的对象,class Myclass里面写的class innerclass只是一个声明

且Myclass的成员没有innerclass的对象,因此只算了_val1的大小(注:不算function1函数的大小,函数存放在公共代码段,不存在对象中)

换句话说,类innerclass(不是对象)定义在Myclass的类域里面,但不占空间


注意:内部类不能直接定义

内部类不能直接定义会报错,需要通过外部类来间接定义内部类

如果只想定义内部类,可以这样写:

cpp 复制代码
#include <iostream>
using namespace std;
class Myclass
{
public:
	class innerclass
	{
	public:
		static int _val2;
	};
};
int Myclass::innerclass::_val2 = 2;

int main()
{
	 Myclass::innerclass innerobj;
	 cout << "innerobj._val2=" << innerobj._val2 << endl;
	 return 0;
}

注意_val2定义的方式,不能将其定义在内部类和外部类之间 ,必须定义在类外(即外部类的外面) ,而且需要使用**两次类名(Myclass::innerclass::)**来定义

运行结果:


如果要加上类型为innerclass的对象大小,**必须将innerclass实例化!**如下:

cpp 复制代码
class Myclass
{
public:
    //......
private:
	int _val1;
	innerclass _obj;//将类实例化
};

再次测试以下代码:

cpp 复制代码
 int main()
{
	Myclass obj;
	cout << sizeof(obj) << endl;
}

运行结果:

注意:这个大小并没有包含static int _val2的大小,因为_val2在静态区,而对象在栈区!

大小为8的计算方法:

Myclass的成员变量有两个:_val1和_obj,_va1占4字节,_obj占1字节(不允许出现空类,这一个字节纯属占位,之前在CD11.【C++ Dev】类和对象(2)文章提到过),4+1==5字节,但要按4字节对齐(最大对齐数的整数倍)以方便访问,因此再填充3个字节凑齐8个字节

有关类的内存对齐的知识点参见下面文章复习:

63.【C语言】再议结构体(上)

64.【C语言】再议结构体(下)

内部类是外部类的友元类

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

那么可以得出:内部类可以访问外部类的非公有成员,代码如下:

cpp 复制代码
#include <iostream>
using namespace std;
class Myclass
{
public:
	void function1()
	{
		cout << "void function1()" << endl;
	}
	class innerclass
	{
	public:
		void function2(const Myclass& myobj)
		{
			cout << "myobj._val1="<<myobj._val1 << endl;
		}
	private:
		static int _val2;
	};
	innerclass _obj;
private:
	int _val1=1;
	
};

 int main()
{
	Myclass obj;
	obj._obj.function2(obj);
}

运行结果:

3.练习

之前在CD26.【C++ Dev】类和对象(17) static成员(下)文章解过JZ64 求1+2+3+...+n,可尝试使用内部类写,下篇将更新代码

相关推荐
angushine11 分钟前
Gateway获取下游最终响应码
java·开发语言·gateway
小乐xiaole13 分钟前
蓝桥杯 2025 C++组 省 B 题解
c++·蓝桥杯·深度优先
西贝爱学习28 分钟前
数据结构:C语言版严蔚敏和解析介绍,附pdf
c语言·开发语言·数据结构
程丞Q香31 分钟前
python——学生管理系统
开发语言·python·pycharm
晓纪同学38 分钟前
C++ Primer (第五版)-第十三章 拷贝控制
java·开发语言·c++
arriettyandray1 小时前
Qt/C++学习系列之QTreeWidget的简单使用记录
c++·qt·学习
dragon_perfect1 小时前
ubuntu22.04上设定Service程序自启动,自动运行Conda环境下的Python脚本(亲测)
开发语言·人工智能·python·conda
LL1681992 小时前
SSM考研助手管理系统
java·服务器·开发语言·数据库·学习
zhaoyqcsdn2 小时前
C++对象池设计:从高频`new/delete`到性能飞跃的工业级解决方案
c++·经验分享·笔记
CppPlayer-程序员阿杜3 小时前
poll为什么使用poll_list链表结构而不是数组 - 深入内核源码分析
网络·c++·链表·list·poll