首页 > 编程语言 >C++ | 友元

C++ | 友元

时间:2023-02-28 12:11:07浏览次数:33  
标签:友元 Remote show TV C++ state class

类的主要特点之一就是数据的封装,即类的私有成员无法在类的外部(作用域之外)进行访问。但是,有时需要在类的外部访问类的私有成员,怎么办?C++提供了另外一种形式的访问权限:友元

1. 友元函数

创建友元函数

创建友元函数的第一步是将其原型放在类声明中,并在原型声明前加上关键字friend

friend void operator(param1, param2 , ... );

该原型意味着两点:

  • 虽然operator()函数是在类声明中声明的,但它不是成员函数,因此不能使用成员运算符来调用;
  • 虽然operator()函数不是成员函数,但它与成员函数的访问权限相同(可访问对象任意成员属性,包括私有属性)。
#include<iostream>
#include<string>
using namespace std;

class TV{
public:
	friend void show(const TV &t); // 类内声明友元函数(friend关键字只出现在声明处)
	enum state_choice{OFF, ON};
	enum volume_range{MinVal=0, MaxVal=10};
	TV():state(OFF), volume(5){}
private:
	int state; // 开/关
	int volume; // 音量
};

void show(const TV &t){
	cout << "TV's state=" << t.state << "  TV's volume=" << t.volume << endl;
}


int main(){
	TV t;
	show(t);

	return 0;
}

输出:

TV's state=0  TV's volume=5

友元声明可以位于共有、私有或保护部分,其所在的位置无关紧要。

2. 友元类

电视:class TV

class TV{
public:
	friend class Remote; // 声明友元类,Remote可以访问TV的私有成员
	enum state_choice{OFF, ON};
	enum volume_range{MinVal=0, MaxVal=10};
	TV():state(OFF), volume(5){}
	void onOff(){ 
		state = (state == ON)?OFF:ON;
		cout << "电视 ";
		if(state == ON)
			cout << "已开启..." << endl;
		else
			cout << "已关闭..." << endl;
		
	}
	bool volUp(){
		if(volume < MaxVal){
			volume++;
			cout << "音量+1." << endl;
			return true;
		}else{
			cout << "您已达最大音量!" << endl;
			return false;
		}
	}
	bool volDown(){
		if(volume > MinVal){
			volume--;
			cout << "音量-1." << endl;
			return true;
		}else{
			cout << "您已达最小音量!" << endl;
			return false;
		}
	}
private:
	int state; // 开/关
	int volume; // 音量
};

遥控:class Remote

class Remote{
public:
	void onOff(TV &t){ t.onOff();}
	bool volUp(TV &t){
		if(t.state == 1){
			return t.volUp();
		}else{
			cout << "电视尚未开启,无法增减音量!" << endl;
			return false;
		}
	}
	bool volDown(TV &t){ 
		if(t.state == 1){
			return t.volDown();
		}else{
			cout << "电视尚未开启,无法增减音量!" << endl;
			return false;
		}
	}
	void show(TV &t){ 
		cout << "电视状态:" << t.state << "\t电视音量:" << t.volume << endl;
	}
};

使用:

int main(){
	TV t;
	Remote r;

	r.volUp(t);
	r.show(t);
	cout << endl;

	r.onOff(t);
	r.show(t);
	cout << endl;

	r.volUp(t);
	r.show(t);
	cout << endl;

	r.onOff(t);
	r.show(t);
    
	return 0;
}

输出:

电视尚未开启,无法增减音量!
电视状态:0      电视音量:5

电视 已开启...
电视状态:1      电视音量:5

音量+1.
电视状态:1      电视音量:6

电视 已关闭...
电视状态:0      电视音量:6

3. 友元成员函数

3.1 循环依赖

如果想让Remove的show方法称为TV类的友元成员函数:

class TV{
    friend void Remote::show(TV &t);
};

然而,要使编译器能够处理这条语句,它必须知道Remote的定义。否则,它无法知道Remote是一个类,而show是这个类的方法。这意味着应将Remote的定义放到TV的定义前面。而Remote的方法show中又提到了TV对象,这意味着TV类定义又要位于Remote定义之前,这就出现了循环依赖的情况。

image

3.2 解决循环依赖

避开这种循环依赖的方法是使用前向声明(forward declaration)。为此,需要在Remote定义的前面插入下面的语句:

class TV; 

这样,排列次序应如下:

class TV; // forward declaration
class Remote{...};
class TV{...};

能否像下面这样排列呢?

class Remote; 
class TV{...};
class Remote{...};

不能!

原因在于,编译器在TV类的声明中看到Remote的一个方法被声明为TV类的友元之前,应该先看到Remote类的声明和show()方法的声明。

还有一个问题尚需解决。那就是Remote的show方法中调用了TV的私有变量state和volume,这表明编译器此时必须已经看到了TV类的声明,这样才知道TV类中有哪些成员,但是TV类的声明又位于Remote之后,解决方法就是使Remote声明中只包含方法声明,具体的方法定义放在TV类之后。完整正确代码如下:

#include<iostream>
#include<string>
using namespace std;
class TV;

class Remote{
public:
	void show(TV &t);
};

class TV{
public:
	friend void Remote::show(TV &t); // 声明友元成员函数
	enum state_choice{OFF, ON};
	enum volume_range{MinVal=0, MaxVal=10};
	TV():state(OFF), volume(5){}
private:
	int state; // 开/关
	int volume; // 音量
};

void Remote::show(TV &t){ 
	cout << "电视状态:" << t.state << "\t电视音量:" << t.volume << endl;	
}


int main(){
	TV t;
	Remote r;

	r.show(t);

	return 0;
}

输出:

电视状态:0      电视音量:5

注意:让整个Remote类称为友元类并不需要前向声明,因为友元语句本身已经指出Remote是一个类:

friend class Remote;

标签:友元,Remote,show,TV,C++,state,class
From: https://www.cnblogs.com/lijiuliang/p/17163570.html

相关文章

  • java调用c++的几种方式
    jni类似c#调用c++的方式,定义java端的c++代码接口。packagecrayon.jni;publicclassJNITest{publicnativestaticvoidset(inti);publicna......
  • KingbaseES libstdc++.so.6 version 'CXXABI_1.3.8'问题处理
    概述initdb报错如下:“ERROR:libstdc++.so.6:version:'CXXABI_1.3.8'notfound(requiredby...)”此文是以CentOSLinux7(AltArch)操作系统为例,编译安装高版本GC......
  • 代码随想录训练营day 2 |977有序数组的平方 209.长度最小的子数组 (C++)
    977、有序数组的平方题目链接:977、有序数组的平方题目描述:给你一个按非递减顺序排序的整数数组nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。例......
  • C++ condition_variable
    一、使用场景在主线程中创建一个子线程去计数,计数累计100次后认为成功,并告诉主线程;主线程收到计数100次完成的信息后继续往下执行二、条件变量的成员函数wait:当前线......
  • C++ 文件知识
    #include"iostream"#include"filesystem"#include"fstream"intcount=0;std::stringMessage="";//读取文件内容,C风格intReadFile(char*FileName,char*......
  • C++调试环境的建立(仅适合初学者,高手莫入)
    近日,发现光用C#还是不行,毕竟许多老代码都是C++等其他语言写的,所以决定学C++。作为初学者,连编译环境等都不了解,问了一些高手,他们推荐N多编译器,我晕了(云里来雾里去的,一点不过......
  • C/C++数据结构课程设计任务书[2023-02-27]
    C/C++数据结构课程设计任务书[2023-02-27]文华学院数据结构课程设计任务书一、 课程设计题目1. 家谱管理系统的设计与实现实现对某家族成员信息的管理,包含建......
  • C++ 遍历目录
    单层遍历目录(无递归):#include<filesystem>namespacefs=std::filesystem;constfs::pathpathToShow{argc>=2?argv[1]:fs::current_path()};for(cons......
  • C++ STL容器
    STL容器STL容器主要有,动态数组vector,循环双向链表list,双端队列deque,栈stack,Vector容器#include<vector>//头文件vector<int>a;//定义了一个int类型的vector......
  • 如何选择Python与C++之间的胶水:Boost.Python,Cython,pybind11,SWIG
    Python作为一门胶水语言,它与C/C++之间的兼容性(Interoperability)我认为是它相比其他动态语言脱颖而出的最大原因。Python原生支持的是与C语言的接口,Python的发行版自带有Pyt......