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

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;
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值