一文带你搞懂C++友元和类型转换

news2024/9/24 7:25:27

6. C++友元

在C++中,一个类中可以有 public、protected、private 三种属性的成员,通过对象可以访问 public 成员,只有本类中的函数可以访问本类的 private 成员。现在,我们来介绍一种例外情况——友元(friend)。借助友元(friend),可以使得其他类中的成员函数以及全局范围内的函数访问当前类的 private 成员。

friend 的意思是朋友,或者说是好友,与好友的关系显然要比一般人亲密一些。我们会对好朋友敞开心扉,倾诉自己的秘密,而对一般人会谨言慎行,潜意识里就自我保护。在 C++ 中,这种友好关系可以用 friend 关键字指明,中文多译为“友元”,借助友元可以访问与其有好友关系的类中的私有成员。如果你对“友元”这个名词不习惯,可以按原文 friend 理解为朋友。

C++友元

C++友元是用friend关键修饰的函数或者类,友元用来打破类封装(忽视权限限定)

  • 友元并不是说直接访问数据成员,友元只是提供一个场所赋予对象具有打破权限限定

  • 友元函数

  • 友元类

  • 友元函数和友元类不属于当前类,实现函数或者类不需要类名限定

友元函数

结合着类的特性,可知:类具有封装和信息隐藏的特性。只有类的成员函数才能访问类的私有成员,程序中的其他函数是无法访问私有成员的。非成员函数可以访问类中的公有成员,但是如果将数据成员都定义为公有的,这又破坏了隐藏的特性。另外,应该看到在某些情况下,特别是在对某些成员函数多次调用时,由于参数传递,类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。

为了解决上述问题,提出一种使用友元的方案。友元是一种定义在类外部的普通函数,但它需要在类体内进行声明,为了与该类的成员函数加以区别,在声明时前面加以关键字friend友元不是成员函数,但是它可以访问类中的私有成员。友元的作用在于提高程序的运行效率,但是,它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。

  • 普通函数成为类的友元函数
#include <iostream>
#include <string>
using namespace std;
class MM
{
public:
	MM(string name, int age, int num) :name(name), age(age), num(num) {}
	void print();
	//友元函数
	friend void visitedData();
	friend void visited(MM mm);
protected:
	int num;
private:
	int age;
	string name;
};
void MM::print() 
{
	cout << name << "\t" << age << "\t" << num << endl;
}
//友元函数
void  visitedData() 
{
	//name = "ILoveyou";   不是直接访问,赋予对象的具有这样权限
	//创建对象的无视权限
	MM  mm("girl", 18, 1001);
	cout << mm.name << "\t" << mm.age << "\t" << mm.num << endl;
	MM* p = new MM("new", 28, 1002);
	cout << p->name << "\t" << p->age << "\t" << p->num << endl;
}
void visited(MM mm) 
{
	cout << mm.name << "\t" << mm.age << "\t" << mm.num << endl;
}
int main() 
{
	MM girl("girl", 19, 1002);
	//girl.name="name";			//类外只能访问public
	girl.print();
	visitedData();
	visited(girl);
	return 0;
}

[说明]

  1. 友元函数可访问类的私有成员,但不是类的成员函数
  2. 友元函数不能用const修饰
  3. 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  4. 一个函数可以是多个类的友元函数
  5. 友元函数的调用与普通函数的调用和原理相同
  • 以另一个类的成员函数为友元函数
#include <iostream>
#include <string>
using namespace std;
//前向声明
class A;
class B
{
public:
	B(int b) :b(b) {}
	void printA(A object);
private:
	int b;
};

class A 
{
public:
	A(int a) :a(a) {}
	friend void B::printA(A object);
private:
	int a;
};

void B::printA(A object)
{
	cout << object.a << endl;
}
int main() 
{
	B b(111);
	A a(222);
	b.printA(a);


	return 0;
}

//A 以B的成员函数为友元函数,B又以A类的成员函数为友元,如果存在这种需求,代码设计有问题,C++允许这种关系

友元类

友元除了友元函数以外,友元还可以是类——友元类,即一个类可以作另一个类的友元。当一个类作为另一个类的友元时,这就意味着这个类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的非公有成员

特性

  • 友元关系是单向的,不具有交换性。

比如Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。

  • 友元关系不能传递

如果B是A的友元,C是B的友元,则不能说明C时A的友元。

  • 友元关系不能被继承,但对已有的方法来说访问权限不改变。
#include <iostream>
#include <string>
using namespace std;
class MM 
{
	friend class Boy;   //声明boy类是MM友元类
public:
	MM(string name) :name(name) {}
private:
	string name;
};

//友元类中,MM类的对象无视权限
class Boy 
{
public:
	Boy() :mm("mm")
	{
		pObject = new MM("Object");
	}
	void print() 
	{
		cout <<"访问私有属性:" << mm.name << endl;
		cout << "访问私有属性:" << pObject->name << endl;
		MM* pMM = new MM("new");
		cout << "访问私有属性:" << pMM->name << endl;
	}
private:
	MM mm;
	MM* pObject;
};
//互为友元类--->知道有这么回事就行,不需要自己写这样代码
class A 
{
public:
	friend class B;
	void printA();

private:
	string a_name="A";
};

class B
{
public:
	friend class A;
	void printB() 
	{
		A a;
		cout << a.a_name << endl;
	}
private:
	string b_name="B";
};

void A::printA() 
{
	B b;
	cout << b.b_name << endl;
}
int main() 
{
	Boy boy;
	boy.print();
	B b;
	b.printB();
	A a;
	a.printA();
	return 0;
}

友元优缺点

利用 friend 修饰符,可以让一些普通函数 或 另一个类的成员函数 直接对某个类的保护成员和私有成员进行操作,提高了程序的运行效率;同时避免把类的成员都声明为public,最大限度地保护数据成员的安全。

但是,即使是最大限度地保护数据成员,友元也破坏了类的封装性。

如果将类的封装比喻成一堵墙的话,那么友元机制就像墙上开了一个门。所以使用友元时一定要慎重。

小试牛刀

//自己写个友元案例,测试打破权限这个功能即可

C++类型转换

构造的方式做转换

#include <iostream>
using namespace std;
int main() 
{
	int num = 1.11;
	cout << num << endl;
	int cnum = (int)1.11;
	cout << cnum << endl;
	int cppnum = int(1.22);		//C++强制类型转换
	cout << cppnum << endl;
	return 0;
}

static_cast类型转换

类似C语言的强制类型转换,按照C++的说法 比C语言的更为安全

  • 基本数据类型的强制转换
  • 空指针转换目标类型指针
  • 不能操作带const属性的类型
//static_cast<要转换的类型>(要转换目标)
//要转换的类型: 数据类型
//要转换目标  可以是表达式,或者常量,都可以
#include <iostream>
using namespace std;

void test_static_cast()
{
	//No.1 基本数据类型的强制转换
	int num = static_cast<int>(1.111);
	//No.2 空类型指针的转换
	double* pD = new double(1.11);
	void* pVoid = static_cast<void *>(pD);
	//No.3 不能做const属性的类型的转换
	//增加const属性
	//不能去掉const属性
	int number = 11;
	const int cNum = static_cast<const int >(number);
	const int ccNum = number;
	const int data = 1;
	int* pData = (int *)(&data);				//C语言强制类型转换
	//int* pcData = static_cast<int*>(&data);		//错误
}
int main() 
{
	test_static_cast();
	return 0;
}

const_cast类型转换

  • 去掉const属性(提供一个可以修改接口去操作const数据类型)
  • 加上const属性(用的少一点)
#include <iostream>
using namespace std;

class Str 
{
public:
	//去掉const属性
	Str(const char* str) :str(const_cast<char*>(str)) {}
	void print() 
	{
		cout << str << endl;
	}
private:
	char* str;
};

void test_const_cast() 
{
	//增加const属性
	const int data = 1;
	int* pData = const_cast<int*>(&data);
	*pData = 1001;		//不会作用到const变量,只是单纯提供一个接口
	cout <<"data:"<< data << endl;
	cout << "*pData:" << *pData << endl;
	cout << &data << endl;
	cout << pData << endl;
	Str str("ILoveyou");    //错误,C++对于const要求更为严格
	str.print();
	char sstr[20] = "ILoveyoud";
	Str str2(sstr);
	str2.print();
   	//3.引用类型
	Test test;
	const Test& c_test = test;
	//c_test.print();		//常属性的对象只能调用常成员函数
	Test& m_test = const_cast<Test&>(c_test);
	m_test.print();
}

int main() 
{
	test_const_cast();


	return 0;
}

reinterpreat_cast类型转换

把指针转换为一个整数,又可以把整数转换为一个指针,指针的效果依然有效(认识知道有这么回事即可)

#include <iostream>
using namespace std;
int Max(int a, int b) 
{
	return a > b ? a : b;
}
//官方案例
unsigned short Hash(void* p) {
	unsigned int val = reinterpret_cast<unsigned int>(p);
	return (unsigned short)(val ^ (val >> 16));
}

void test_reinterpret_cast() 
{
	int* p = reinterpret_cast<int*>(0);   //p=nullptr;
	//官方案例
	int a[20];
	for (int i = 0; i < 20; i++)
		cout << Hash(a + i) << endl;

	//允许将任何指针转换为任何其他指针类型。 也允许将任何整数类型转换为任何指针类型以及反向转换
	int* num = reinterpret_cast<int*>(Max); //把函数地址转换为int类型的数字
	cout << *num << endl;
	auto pMax = reinterpret_cast<int(*)(int, int)>(num);
	cout << "max:" << pMax(1, 2) << endl;	
}
int main() 
{
	test_reinterpret_cast();
	return 0;
}

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

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

相关文章

【ai】Easy-RAG 6:安装bge-reranker-large、 成功部署并通过frpc访问

作者用到了funasrwhisper还有RAG的rerank模型这里用的是bge-reranker-large , 要从 https://huggingface.co/BAAI/bge-reranker-large 下载。AttributeError: module ‘numba’ has no attribute ‘jit’ 【ai】Easy-RAG 4: 修复依赖项:numpy numba omegaconf 等作者的地址是…

英特尔宣布针对对Llama 3.1进行优化 以提升所有产品的性能

日前Meta正式发布了Llama 3.1开源大模型&#xff0c;以其庞大的参数量和卓越性能&#xff0c;首次在多项基准测试中击败了GPT-4o等业界领先的闭源模型。允许开发者自由地进行微调、蒸馏&#xff0c;甚至在任何地方部署&#xff0c;这种开放性为AI技术的普及和创新提供了无限可能…

关于promise的一些例题(运行步骤详细说明)

关于promise的一些例题(详细说明) 基本例题 // 直接运行 输出 1 2 const promise new Promise((resolve, reject) > {console.log(1);resolve();console.log(2); });// then后面放入微队列 promise.then(() > {console.log(3); });// 输出4 之后没有代码了所以运行为队…

秋招突击——7/23——百度提前批面试准备和正式面试

文章目录 引言一面准备面试预演一1、讲一下hashcode()和equals()关系2、equals()和有什么区别3、讲一下重载和重写的区别4、讲一下深拷贝、浅拷贝的区别5、讲一下Java异常的基类&#xff0c;运行时异常举几个例子&#xff0c;什么情况下会出现&#xff1f;6、讲一下Java中线程的…

【管控业财一体化】

1. 引言 大型集团在现代企业管理中扮演着举足轻重的角色&#xff0c;其管控业财一体化解决方案是实现企业高效运营的关键。随着数字化转型的加速&#xff0c;业财一体化不再局限于财务与业务流程的简单融合&#xff0c;而是向着更深层次的数据驱动、智能化决策和价值创造方向发…

光猫设置桥接 路由器pppoe拨号 设置正常访问光猫 (openwrt)

网络信息展示 光猫桥接很简单吧&#xff0c;就不说了。先来列出修改前的网络接口和网络信息。 光猫192.168.1.1&#xff0c;openwrt 10.0.0.0/8 初始配置 需要记录的信息&#xff1a;WAN的网络设备&#xff08;eth1&#xff09;&#xff0c;光猫的IP&#xff08;192.168.1.1&am…

RV1126 Linux 系统,接外设,时好时坏(一)应该从哪些方面排查问题

在 Linux 系统中接外设时,遇到“时好时坏”的问题,可能是由多种因素引起的。以下是一些排查问题的建议。 1. 硬件方面的排查 1.1 连接检查 物理连接: 确保外设与主板之间的连接良好,检查插头、插座及线缆是否牢固。引脚配置: 确认设备树中引脚的配置是否正确,尤其是引脚…

linux modetest常用命令

1 通过help有哪些参数 130|:/ # modetest -h usage: modetest [-acDdefMPpsCvrw]Query options:-c list connectors-e list encoders-f list framebuffers-p list CRTCs and planes (pipes)Test options:-P <plane_id><crtc_id>:<w>x&l…

Web开发:ASP.NET CORE中前端使用Ajax定时获取后端数据

一、低难度&#xff08;刷新a标签&#xff09; 1、需求 给a标签每15s刷新一次&#xff0c;显示最新的时间&#xff08;时间必须由后端获取&#xff09; 应该如何操作呢 2、代码 后端 using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Mi…

go语言开发Prometheus Exporter(DM数据库)

一、介绍 源码步骤基于dameng_exporter源码讲解&#xff0c;看完本篇文章可以直接进行二次开发。 dameng exporter的开源地址&#xff1a;https://github.com/gy297879328/dameng_exporter&#xff08;可直接对接prometheusgrafana 提供表盘&#xff09; 开发一个exporter 其…

智谱OpenDay“大有可玩”:30秒将任意文字生成视频

Sora毫无疑问带来AI大模型的全新玩法&#xff0c;大模型可基于任意文字生成视频&#xff0c;这也是这个“大家庭”若干努力&#xff08;包括Runway的Gen系列、微软的Nuwa、Meta的Emu、谷歌的Phenaki/VideoPoet、CogVideo等&#xff09;的一个全新高度。 7月26日&#xff0c;这…

数据结构(5.3_4)——线索二叉树的概念

普通二叉树找某结点前驱和后继的方法 中序线索二叉树 n个结点的二叉树&#xff0c;有n1个空链域!可用来记录前驱&#xff0c;后继的信息 中序线索二叉树的存储结构 //线索二叉树结点 typedef struct ThreadNode {ElemType data;struct BiTNode* lchild, * rchild;int ltag,…

Git、Gitlab以及分支管理

分布式版本控制系统 一、Git概述 Git是一种分布式版本控制系统&#xff0c;用于跟踪和管理代码的变更。它由Linus torvalds创建的&#xff0c;最初被设计用于Linux内核的开发。Git 允许开发人员跟踪和管理代码的版本&#xff0c;并且可以在不同的开发人员之间进行协作。 Githu…

【学习笔记】解决Serial Communication Library编译问题

【学习笔记】解决编译 Serial Communication Library 时的 Catkin 依赖问题 Serial Communication Library 是一个用 C 编写的用于连接类似 rs-232 串口的跨平台库。它提供了一个现代的 C 接口&#xff0c;它的工作流程设计在外观和感觉上与 PySerial 相似&#xff0c;但串口速…

操盘手分享:葆奕康平台获上市公司投资3300万,大健康新零售模式!

文丨微三云胡佳东&#xff0c;点击上方“关注”&#xff0c;为你分享市场商业模式电商干货。 - 2024年的项目越来越难做&#xff0c;不是随便开个盘在线上零撸、嘘投、空转&#xff08;无实业&#xff09;&#xff0c;这种没实力、没资本、没实业、没生态的项目90%的都活不过…

linux如何卸载python3.5

卸载&#xff1a; 1、卸载python3.5 sudo apt-get remove python3.5 2、卸载python3.5及其依赖 sudo apt-get remove --auto-remove python3.5 3、清除python3.5 sudo apt-get purge python3.5 或者 sudo apt-get purge --auto-remove python3.5

LeetCode 2844.生成特殊数字的最少操作(哈希表 + 贪心)

给你一个下标从 0 开始的字符串 num &#xff0c;表示一个非负整数。 在一次操作中&#xff0c;您可以选择 num 的任意一位数字并将其删除。请注意&#xff0c;如果你删除 num 中的所有数字&#xff0c;则 num 变为 0。 返回最少需要多少次操作可以使 num 变成特殊数字。 如…

第二证券:台风的后遗症竟然是台股!

今日早盘&#xff0c;中国台湾的台湾加权指数开盘大跌逾4%&#xff0c;台积电跌超6%。此前&#xff0c;因为飓风的原因&#xff0c;台湾股市暂停生意两天。暂停生意前&#xff0c;台湾加权指数收报22871点&#xff0c;上升614点&#xff0c;涨幅高达2.76%。 剖析人士认为&…

opencv - py_calib3d - py_epipolar_geometry 对极几何

文章目录 Epipolar Geometry 对极几何目标基本概念代码练​​习 Epipolar Geometry 对极几何 目标 在本节中&#xff0c; 我们将学习多视图几何的基础知识我们将了解什么是对极点、对极线、对极约束等。 基本概念 当我们使用针孔相机拍摄图像时&#xff0c;我们会丢失一个…

解锁人工智能学习中的数学密钥

一、启航&#xff1a;奠定数学基础 1. 线性代数&#xff1a;AI的入门语言 学习目标&#xff1a;掌握向量、矩阵的基本概念及运算&#xff0c;理解线性空间、线性变换及特征值、特征向量的意义。学习建议&#xff1a;从基础教材入手&#xff0c;如《线性代数及其应用》&#x…