IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++9 --- 友元 -> 正文阅读

[C++知识库]C++9 --- 友元

一、友元

1、为什么需要友元这个概念

想要从外部,这里是通过Add函数访问私有数据成员,又不想通过接口(共有函数)去访问
可能有人说友元破坏了类的封装性,但还是看设计时的需求和要求。

class A
{
public:
	A(int i = 0):m_i(i){}
	friend void Add(A& a,A& b);//将Add函数作为类A的友元,不能再类A中去定义
protected:
	//friend void Add(A& a,A& b);//不管放在哪,都可实现友元
	int m_j;//也可访问
private:
	//friend void Add(A& a,A& b);
	int m_i;
};
void Add(A& a,A& b)
{
	cout << a.m_i << endl;
	cout << b.m_i << endl;

	cout << a.m_i + b.m_i << endl;
	//cout <<a.GetI() + b.GetI() <<endl;
}
void main()
{
	A a(5);
	A b(8);
	Add(a,b);
}

运行结果:
在这里插入图片描述

2、什么是友元

友元 – 让函数或者类作为另一个类的朋友,则可以访问当前类的private或者protected成员
是单向的

友元 friend 机制允许一个类授权其他的函数访问它的非公有成员。

友元声明以关键字 friend 开头,它只能出现在类的声明中,它们不受其在类体中的public private 和 protected 区的影响.

友元分为外部函数友元,成员函数友元,类友元。

友元的特点:
1)不具有对称性:A是B的友元.并不意味着B是A的友元。
2)不具有传递性:A是B的友元,B是C的友元,但A不是C的友元。
3)不具有继承性::Base类型继承Object类型,如果Object类型是A的友元,但Base类型不是A友元。

3、友元的三种情况:

1)一个普通函数作为类的友元,那么在当前函数中就可以通过对象访问类的private或者protected

注意:这个函数只能在外部定义,在当前类中引用即可

代码示例:

class B;//需要事先进行 - 前项引用声明,实际上没有定义
class C;
class A
{
public:
	A(int i = 0):m_i(i){}
	friend int Sum(A& a,B& b,C& c);//会报错,因为在定义类A的时候,没有定义B,需要使用前项引用声明
private:
	int m_i;
};
class B
{
public:
	B(int j = 0):m_j(j){}
	friend  int Sum(A& a,B& b,C& c);
private:
	int m_j;
};
class C
{
public:
	C(int k = 0):m_k(k){}
	friend int Sum(A& a,B& b,C& c);
private:
	int m_k;
};
int Sum(A& a,B& b,C& c)
{
	return a.m_i + b.m_j + c.m_k;
}
void main()
{
	A a(10);
	B b(20);
	C c(30);
	cout << Sum(a,b,c) << endl;
}

运行结果:

在这里插入图片描述

2)一个类的成员函数最为另一个类的友元

注意:成员函数建议放在类外定义,

class A;
class C;
class B
{
public:
	B(int j = 0):m_j(j){}
	 void Sub(A& a,C &c);//这里需要在类外定义,只能进行声明,因为要对Sub进行定义,那其中的B和A类都需要定义,实际上没有定义,所以不能实现,需要在类外定义
private:
	int m_j;
};
class A
{
public:
	A(int i = 0):m_i(i){}
	//friend void Sub(A& a);//error,这句话的意思的将一个普通函数作为类的友元
	friend void B::Sub(A& a,C &c);
private:
	int m_i;
};
class C
{
public:
	C(int k = 0):m_k(k){}
	friend void B::Sub(A& a,C &c);
private:
	int m_k;
};
void B::Sub(A& a,C &c)
{
	cout << a.m_i - c.m_k - m_j << endl;
}
void main()
{
	A a(40);
	B b(20);
	C c(10);
	b.Sub(a,c);
}

运行结果:

在这里插入图片描述

3)一个类作为类外一个类的友元类

一个类A作为类外一个类B的友元类,则A的所有成员函数就可以访问B的私有数据成员
使用情况示例:当前类内函数需要大量使用另一个类的私有数据成员

//B和A互为朋友
class B;
class C;
class A
{
public:
	A(int a = 0) :m_a(a) {}
	void print(B& b);
	void test(B& b);
	void show(B& b);
	void print(C& c);
private:
	int m_a;
};
class B
{
public:
	B(int b = 0) :m_b(b) {}
	friend class A;
private:
	int m_b;
};
class C
{
public:
	C(int c = 0) :m_c(c) {}
	friend class A;
private:
	int m_c;
};
void A::print(B& b)
{
	cout << "A::print b "<<b.m_b << endl;
}
void A::test(B& b)
{
	cout << "A::test "<<b.m_b << endl;
}
void A::show(B& b)
{
	cout << "A::show " << b.m_b << endl;
}
void A::print(C& c)
{
	cout << "A::print c "<<c.m_c << endl;
}
void main()
{
	B b(30);
	A a(40);
	C c(10);

	a.print(b);
	a.show(b);
	a.test(b);
	a.print(c);
}

运行结果:

在这里插入图片描述
B和A互为朋友:

class B;
class C;
class A
{
public:
	A(int a = 0) :m_a(a) {}
	void print(B& b);
	void test(B& b);
	void show(B& b);
	void print(C& c);
	friend class B;
private:
	int m_a;
};
class B
{
public:
	B(int b = 0) :m_b(b) {}
	friend class A;
	void Print(A& a);
private:
	int m_b;
};

void B::Print(A& a)
{
	cout << "B::print a "<< a.m_a << endl;
}
class C
{
public:
	C(int c = 0) :m_c(c) {}
	friend class A;
private:
	int m_c;
};
void A::print(B& b)
{
	cout << "A::print b "<<b.m_b << endl;
}
void A::test(B& b)
{
	cout << "A::test "<<b.m_b << endl;
}
void A::show(B& b)
{
	cout << "A::show " << b.m_b << endl;
}
void A::print(C& c)
{
	cout << "A::print c "<<c.m_c << endl;
}
void main()
{
	B b(30);
	A a(40);
	C c(10);

	a.print(b);
	a.show(b);
	a.test(b);
	a.print(c);

	b.Print(a);
}

运行结果:

在这里插入图片描述

4、友元注意点:

1)友元是单向的

2)友元是不能传递的

3)友元是不能继承的

对于以下代码:
BB是AA的朋友,CC是BB的朋友,如果CC没有作为AA的友元类,则CC和AA没有关系 — 即友元不能传递
CC能不能访问AA的私有数据成员? 不能

class BB;
class CC;
class AA
{
public:
	friend class BB;
private:
	int m_a;
};
class BB
{
public:
	friend class CC;
	void Show(AA& a);
private:
	int m_b;
};
class CC
{
public:
	void print(BB& b);
	void test(AA& a);
};
//
void CC::test(AA& a)
{
	//cout << a.m_a << endl;  //error  CC不是AA的朋友
}
void BB::Show(AA& a)
{
	cout << a.m_a << endl;
}
void CC::print(BB& b)
{
	cout << b.m_b << endl;
}
void main()
{
	//AA a;
	BB b;
	CC c;
	c.print(b);
}

运行结果:

在这里插入图片描述

运行 cout << a.m_a << endl; 时会报错:

在这里插入图片描述

5、相关应用 – 实现 cout << a + b << endl; 的重载实现(a,b为类)

1)欲实现代码(实现两个不同类的相加):

//error
class A
{
public:
	A(int i = 0):m_i(i){}
private:
	int m_i;
};
class B
{
public:
	B(int j = 0):m_j(j){}
private:
	int m_j;
};
void main()
{
	A a(10);
	B b(20);
	cout << a + b << endl;
}

当前实现不了
在这里插入图片描述

其中cout << a + b << end;可解读为//a.+(b) 或 +(a,b)

2)两个不是同一类型的对象要进行 + 的前提:

只有两个类其中的数据成员的个量、数据成员的类型一致的情况下,才能去尝试去进行相加。

3)代码实现示例:

class B;
class A
{
public:
	A(int i = 0):m_i(i){}
	int operator+(B& b);
private:
	int m_i;
};
class B
{
public:
	B(int j = 0):m_j(j){}
	friend int A::operator+(B& b);
private:
	int m_j;
};
int A::operator+(B& b)
{
	return m_i + b.m_j;
}

void main()
{
	A a(10);
	B b(20);
	cout << a + b << endl; // a.+(b) 或  +(a,b)
}

运行结果:

在这里插入图片描述

6、运算符重载重载成友元形式

一般情况下运算符重载重载成友元形式是以下这种情况

对于以下基本的+运算符重载示例

注意:
重载成成员函数可以将第一个操作数省略,
重载成友元函数不能省略第一个操作数,是几个操作数就写几个操作数

重载成成员函数形式 – a.+(b)

class A
{
public:
	A(int i = 0):m_i(i){}
	void print()
	{
		cout << m_i << endl;
	}
	A operator+(A& b)
	{
		return m_i + b.m_i;
	}
private:
	int m_i;
};
void main()
{
	A a(5);
	A b(10);
	(a + b).print();//可以解析成 a.+(b) 或 +(a,b)
}

运行结果:
在这里插入图片描述

重载成 普通函数 – +(a,b) ,这里重载成友元函数

友元必须将两个参数都有

class A
{
public:
	A(int i = 0):m_i(i){}
	void print()
	{
		cout << m_i << endl;
	}
	A operator+(A& b)
	{
		return m_i + b.m_i;
	}
	friend A operator-(A& a,A& b);//重载成友元函数
private:
	int m_i;
};
A operator-(A& a,A& b)//两个参数都需要
{
	return a.m_i - b.m_i;
}
void main()
{
	A a(5);
	A b(10);
	(a + b).print();//a.+(b) +(a,b)
	(a - b).print();//-(a,b)
}

运行结果:

在这里插入图片描述

前置++和后置++的不同重载

class A
{
public:
	A(int i = 0):m_i(i){}
	void print()
	{
		cout << m_i << endl;
	}
	A operator+(A& b)
	{
		return m_i + b.m_i;
	}
	friend A operator-(A& a,A& b);
	A operator++(int) //重载后a++
	{
		
		int t = m_i;
		m_i = m_i + 1;
		return t;
	}
	friend A& operator++(A& a);
private:
	int m_i;
};
A& operator++(A& a)
{
	++a.m_i;
	return a;
}
A operator-(A& a,A& b)
{
	return a.m_i - b.m_i;
}
void main()
{
	A a(5);
	A b(10);
	//(a + b).print();//a.+(b) +(a,b)
	//(a - b).print();//-(a,b)
	(a++).print();//a.++()
	(++b).print();
}

运行结果:

在这里插入图片描述

7、对输出运算符cout的重载实现

欲实现 A a(5); cout<< a <<endl;`

1)思考:

怎么去重载输出运算符?类比于(a + b).print();//a.+(b) +(a,b)
重载输出时返回类型是什么类型的?
输出能不能作为左边,是值返回还是引用返回?

对于代码:`cout<< a <<endl;``

可以解析为:cout.<<(a) 或者 <<(cout,a)

2)相关知识点

cout -是- ostream类的对象,输出流类
cin -是- istream类的对象,输入流类
在这里插入图片描述
在这里插入图片描述

对于 cout << a;

一般情况下运算符可以解析成下面两种形式

(1)cout.<<(a) ostream类中重载了<<,程序员不能修改
(2)<<(cout,a) 可以在程序员自己定义的类中将<<重载成友元

返回值为引用

对于 cout<<a<<b;
cout<<a 这个表达式作为了上面的表达式<<左边 、
即(cout<<a)<<b
所以返回值为引用

返回值为ostream类型

(cout<<a) << b 表达式执行完后,还能继续用<<,说明返回值为ostream类型,因为只有ostream类型才能使用<<

3)使用模板

friend ostream& operator<<(ostream &out,A &a)//除了第二个参数,其他都是定死的

4)使用示例

class A
{
public:
	A(int i = 0):m_i(i){}
	void print()
	{
		cout << m_i << endl;
	}
	A operator+(A& b)  //this
	{
		return m_i + b.m_i;
	}
	/*
	//较高的编译器,可能会对上一个重载进行报错,可用如下重载
	//也可将声明函数的A& a的&去掉,但不推荐
	A& operator+(A& b)  //this
	{
		m_i = m_i + b.m_i;
		return m_i + b.m_i;
	}
	*/
	friend ostream& operator<<(ostream& out, A&a);
private:
	int m_i;
};
//cout<<a<<b;  <<(cout,a) <<(cout,b)
ostream& operator << (ostream & out, A&a)
{
	out << a.m_i;
	return out;
}
void main()
{
	A a(5);
	A b(10);
	A c(20);
	cout << a << endl;  //5

	c = a + b; //15
	cout << c << endl;//15

	cout << (a + b) << endl;//15
}

运行结果:

在这里插入图片描述

5)重载运算符的相关使用规则

从语法上讲,运算符既可以定义为全局函数,也可以定义为成员函数。文献[Murray ,p44-p47]对此问题作了较多的阐述,并总结了表8-4-1的规则。
在这里插入图片描述
由于C++语言支持函数重载,才能将运算符当成函数来用,C语言就不行。我们要以平常心来对待运算符重载。
(1)不要过分担心自己不会用,它的本质仍然是程序员们熟悉的函数。
(2〉不要过分热心地使用,如果它不能使代码变得更加易读易写,那就别用,否则会自找麻烦。

不能被重载的运算符

在C++运算符集合中,有一些运算符是不允许被重载的。这种限制是出于安全
方面的考虑,可防止错误和混乱。
(1)不能改变C++内部数据类型(如int, fl oat 等)的运算符。
(2)不能重载‘ . ’,因为 ‘.’ 在类中对任何成员都有意义,已经成为标准用法
(3)不能重载目前C++运算符集合中没有的符号,如#,@,$等。原因有两点,一是难以理解,二是难以确定优先级。
(4)对已经存在的运算符进行重载时,不能改变优先级规则,否则将引起混乱。

相关习题:
编写类 String的构造函数、析构函数和赋值函数
已知类String的原型为:

class String
{
public:
	String(const char *str = NULL);            //普通构造函数
	String(const String &other);               //拷贝构造函数
	~String(void);                             //析构函数
	String & operator =(const String &other);  //赋值函数
private:
char *m_data;    //用于保存字符串
};

请编写String的上述4个函数

答案:

//String的析构函数
String::~String(void)
{
	delete[]m_data;
	//由于m_data 是内部数据类型,也可以写成 delete m_data;
}
//String 的普通构造函数
String::String(const char *str)
{
	if(str == NULL)
	{
		m_data = new char[1]; //若能加 NULL 判断则更好
		*m_data = '\0';
	}
	else
	{
		int length = strlrn(str);
		m_data = new char[length + 1]; //若能加 NULL 判断则更好
		strcpy(m_data,str);
	}
}
//拷贝构造函数
String & String::operate =(const String &other)
{
	//(1)检查自赋值
	if(this == &other)
		return *this;
		
	//(2)释放原有的内存资源
	delete[]m_data;
	
	//(3)分配新的内存资源,并赋值内容
	int length = strlen(other.m_data);
	m_data = new char[length + 1];   //若能加 NULL 判断则更好
	strcpy(m_data,other.m_data);
	
	//(4)返回本对象的引用
	return *this;
}
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-11-05 00:06:35  更:2022-11-05 00:09:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 4:49:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码