您现在的位置是:首页 >技术教程 >C/C++ 友元(Friend)机制的利弊关系【友元函数、友元类】网站首页技术教程
C/C++ 友元(Friend)机制的利弊关系【友元函数、友元类】
前言:
友元(Friend)是C++中的一个特殊机制,它可以实现在某些情况下,一个类的私有成员可以被其他类或者函数访问,从而保证代码的灵活度和可维护性。本篇文章将全面介绍C++中的友元机制,包括友元函数、友元类、友元机制的深入解析以及友元机制的利弊分析和总结。
本篇中有大量代码案例,建议跟着代码多敲几遍(实践出真知,弄清楚使用的基本场景就OK)!
目录
一、友元函数
友元函数是指可以直接访问类的私有成员的非成员函数。在C++中,我们可以通过使用“friend”关键字将非成员函数声明为友元函数。在一个类中,友元函数不是这个类的成员函数,但是可以直接访问这个类的私有成员。下面是一个简单的例子,其中类A中的私有成员可以被函数addition访问。
class A {
private:
int a;
public:
A() : a(0) {}
friend int addition(A a1, A a2);
};
int addition(A a1, A a2) {
return a1.a + a2.a;
}
int main() {
A obj1, obj2;
obj1.a = 10;
obj2.a = 20;
cout << "Sum of two values are : " << addition(obj1, obj2);
return 0;
}
上述示例中,我们定义了一个名为addition的函数,并且将类A声明为它的友元。这个函数可以直接访问类A中的私有成员a,并返回它们的和。在main函数中,我们可以创建两个对象obj1和obj2,并为它们的私有成员a分别设置值,最后调用addition函数输出它们的和。
二、友元类
与友元函数类似,友元类也可以直接访问一个类的私有成员。不同的是,友元类是一个类,可以直接访问被它声明为友元的类的私有成员。同样的,“friend”关键字也可以将一个类声明为另一个类的友元。
下面是一个示例,其中类B被声明为类A的友元,从而可以直接访问类A的私有成员a。
class A {
private:
int a;
public:
A() : a(0) {}
friend class B;
};
class B {
public:
void display(A obj) {
cout << obj.a;
}
};
int main() {
A obj;
B demo;
demo.display(obj);
return 0;
}
在上述示例中,我们定义了两个类A和B,并将B声明为A的友元类。类B中的display函数可以直接访问类A的私有成员a,并输出它的值。在main函数中,我们可以创建一个A类对象obj和一个B类对象demo,并调用demo对象的display函数输出obj的私有成员a的值。
需要注意的是,与友元函数不同,友元类的声明需要在类的花括号内部进行声明,而不是在外部进行声明。
三、友元机制深入解析
友元机制是C++中比较特殊且相对较少使用的特性之一。它可以实现某些情况下的灵活编程,但在使用时需要注意一些细节问题。
首先,友元机制破坏了C++中的封装特性。在一个类中,私有成员被设置为private是为了防止类外的对象直接访问它们。但是使用友元机制可以让类外的对象直接访问私有成员,因此友元机制也被认为是一种破坏了封装的特性。为了避免滥用友元机制破坏封装特性,我们需要对友元机制进行谨慎地处理和使用。
其次,友元函数可以是全局函数、类的静态成员函数以及其他类的成员函数。而友元类可以访问被它声明为友元的类的私有成员。因此,友元机制可以实现不同类之间的信息交流和协作。
在使用友元机制时,需要注意以下几点:
- 友元机制应该只用于必要的情况。过多的友元声明会破坏封装和安全性,使得代码难以维护。
- 在定义和实现友元函数时,需要使用类的对象作为参数并返回一个值。这样才能在友元函数中访问类的私有成员。例如:friend int function(A obj);
- 尽量避免对整个类进行友元声明。如果需要访问某个类的私有成员,应该尽量将它们封装成公有接口,而不是暴露私有成员。
- 友元函数和友元类的声明可以写在类的公有、保护或私有访问说明符下面,这是由需求而定的。但是一般情况下,最好将友元声明写在私有访问说明符下面,以达到封装私有成员的目的。
- 友元关系不能被继承,也不能被传递。如果派生类想要访问其基类的私有成员,应该使用虚函数和保护成员函数的方式。
下面是一个综合示例的友元相关代码。
#include<iostream>
using namespace std;
class B;
class A{
private:
int numA;
friend void add(A, B);
public:
A(int n=0) : numA(n) {}
};
class B{
private:
int numB;
friend void add(A, B);
public:
B(int n=0) : numB(n) {}
};
void add(A objectA, B objectB){
cout << "Adding data of A and B objects to a new object C." << endl;
cout << "Sum of private integers of A and B objects is : " << objectA.numA + objectB.numB << endl;
}
int main(){
A objectA(7);
B objectB(11);
add(objectA, objectB);
return 0;
}
在上面的示例中,我们定义了两个类A和B,它们都包含一个私有整数成员,expressed as numA and numB。这两个私有成员都由另一个函数add访问,因此它们在A和B类的定义中都声明为友元函数。我们定义了add函数,该函数接受A和B对象作为参数,并输出它们的和。
在main函数中,我们创建了一个A对象objectA和B对象objectB,并将它们传递给add函数。add函数将访问objectA和objectB的私有成员,并将它们相加输出结果。
除了使用友元函数之外,我们还可以使用友元类来访问另一个类的私有成员。下面是一个使用友元类的示例:
#include<iostream>
using namespace std;
class A;
class B{
public:
void display(A&);
};
class A{
private:
int numA;
public:
A(int n=0) : numA(n) {}
friend class B;
};
void B::display(A &objectA){
cout << "A::numA= " << objectA.numA << endl;
}
int main(){
A objectA(12);
B objectB;
objectB.display(objectA);
return 0;
}
在上面的示例中,我们定义了两个类A和B,其中B被定义为A的友元类。在A类中,我们声明了一个私有整数成员numA,可以被B类访问。在B类中,我们定义了一个display函数,该函数接受A对象作为参数,并输出它的私有成员numA。
在main函数中,我们创建了一个A对象objectA和B对象objectB,并将objectA作为参数传递给objectB的display函数。display函数将访问objectA的私有成员numA,并输出结果。
需要注意的是,在上面的示例中,类B和函数display都被声明为public。这是因为,如果我们将它们声明为private或protected,它们将不能被main函数中的代码访问,从而导致编译错误。
四、利弊小结
利:
1.提高代码的灵活性
在某些情况下,如果没有友元机制,就不能实现一些特殊的需求。例如,如果想要通过一个非成员函数来访问一个类的私有成员,而不是通过成员函数的方式,就需要使用友元函数。
下面的代码示例演示了如何使用友元函数实现在类外部访问类的私有成员,提高代码的灵活性:
#include <iostream>
using namespace std;
class A{
private:
int numA;
public:
A(int n=0) : numA(n) {}
friend int getNumA(A);
};
int getNumA(A objectA){
return objectA.numA;
}
int main(){
A objectA(7);
cout << "The private integer value of objectA is: " << getNumA(objectA) << endl;
return 0;
}
在这个示例中,我们定义了一个类A和一个友元函数getNumA,getNumA函数可以直接访问A类的私有成员numA,从而实现在类外部直接访问类的私有成员。这样,我们就能够实现了一些特殊需求,而不需要通过额外的公有接口来实现,提高了代码的灵活性。
2.实现不同类之间的协作
在实现复杂的程序时,不同的类之间需要进行信息交流和协作。使用友元机制可以实现不同类之间的信息交流和协作,简化程序设计和实现。
下面的代码示例演示了如何使用友元类实现不同类之间的协作:
#include <iostream>
using namespace std;
class A{
private:
int numA;
public:
A(int n=0) : numA(n) {}
friend class B;
};
class B{
private:
int numB;
public:
B(int n=0) : numB(n) {}
void displayNumA(A objectA){
cout << "The value of numA is " << objectA.numA << endl;
}
};
int main(){
A objectA(7);
B objectB(11);
objectB.displayNumA(objectA);
return 0;
}
在这个示例中,我们定义了两个类A和B,A类包含一个私有成员numA,而B类需要访问A的私有成员numA。因此,我们将B声明为A的友元类,从而实现B可以访问A的私有成员。这样,我们就实现了不同类之间的协作,方便了代码的实现。
3.减少代码的复杂度
使用友元机制可以减少代码的复杂度和重复性,使得代码更易于维护和阅读。
下面的代码示例演示了如何使用友元类来减少代码的复杂度:
#include <iostream>
using namespace std;
class A{
private:
int numA;
public:
A(int n=0) : numA(n) {}
friend class B;
};
class B{
private:
int numB;
public:
B(int n=0) : numB(n) {}
void displayNumA(A objectA){
cout << "The value of numA is " << objectA.numA << endl;
}
};
int main(){
A objectA(7);
B objectB(11);
objectB.displayNumA(objectA);
return 0;
}
在这个示例中,我们定义了两个类A和B,A类包含一个私有成员numA,同时B类需要访问A类的numA成员。如果不用友元类,就必须先定义一个公有接口才能在B中访问A的numA成员,这将增加代码的复杂度。但是通过友元类,B可以直接访问A的私有成员,从而减少了代码的复杂度。
4.实现对类的封装和安全性控制
友元机制可以被用于对类的封装和安全性控制。通过友元机制,可以实现只有特定类或者函数可以访问私有成员,从而实现对类的封装和安全性控制。
下面的代码示例演示了如何实现对类的封装和安全性控制:
#include<iostream>
using namespace std;
class A{
private:
int numA;
friend void add(A, int);
public:
A(int n=0) : numA(n) {}
};
void add(A objectA, int num){
objectA.numA += num;
}
int main(){
A objectA(7);
add(objectA, 11);
return 0;
}
在这个示例中,我们定义了一个类A和一个友元函数add。在类A中,numA被声明为私有成员,因此在类外部无法直接访问。但是由于add函数被声明为类A的友元函数,因此它可以直接访问A的私有成员numA。在main函数中,我们创建了一个A对象并将其作为add函数的参数进行调用,add函数修改了A对象的私有成员numA。但是由于add函数仅仅是一个友元函数,并且仅访问A对象的私有成员numA,因此对A类的封装和安全性控制得到了保持。
弊:
1.破坏C++的封装特性
在一个类中,私有成员被设置为private是为了防止类外的对象直接访问它们。但是使用友元机制可以让类外的对象直接访问私有成员,破坏了C++的封装特性。
下面的代码示例演示了如何使用友元函数破坏C++的封装特性:
#include <iostream>
using namespace std;
class A{
private:
int numA;
public:
A(int n=0) : numA(n) {}
friend void setNumA(A, int);
};
void setNumA(A objectA, int n){
objectA.numA = n;
}
int main(){
A objectA(7);
setNumA(objectA, 11);
cout << "The private integer value of objectA is: " << objectA.getNumA() << endl;
return 0;
}
在这个示例中,我们定义了一个类A和一个友元函数setNumA,该函数可以直接访问A的私有成员numA。但是通过友元函数setNumA,用户可以绕过A类提供的公有接口,直接修改其私有成员numA,从而破坏了A类的封装特性,影响了代码的稳定性和可维护性。
2.友元机制容易滥用
过多的友元声明会破坏封装和安全性,使得代码难以维护。因此,友元机制应该只用于必要的情况。否则会导致程序的维护成本增加,同时也会降低程序的安全性和稳定性。
下面的代码示例演示了如何使用友元函数滥用友元机制:
#include<iostream>
using namespace std;
class A{
private:
int numA;
friend void add(A&, int);
public:
A(int n=0) : numA(n) {}
int getNum() { return numA; }
};
void add(A& objectA, int num){
objectA.numA += num;
}
int main(){
A objectA(7);
add(objectA, 11);
cout << "The integer value of numA is : " << objectA.getNum() << endl;
return 0;
}
在这个示例中,我们定义了一个类A和一个友元函数add。add函数通过引用的方式修改A的私有成员numA。但是add函数可以直接访问A的私有成员,这容易导致滥用友元机制,从而影响代码的清晰性和可读性。
3.友元机制可能导致代码耦合性增加
友元机制可能会导致不同类之间的耦合性增加,从而影响程序的可重用性和可扩展性。友元机制需要谨慎地使用,以避免导致代码的耦合性增加。
下面的代码示例演示了如何使用友元函数增加代码耦合性:
#include<iostream>
using namespace std;
class A{
private:
int numA;
friend void add(A&, int);
public:
A(int n=0) : numA(n) {}
void displayNumA() { cout << "The integer value of numA is : " << numA << endl;}
};
void add(A& objectA, int num){
objectA.numA += num;
}
class B{
public:
void display(A& objectA){
objectA.displayNumA();
}
};
int main(){
A objectA(7);
B objectB;
add(objectA, 11);
objectB.display(objectA);
return 0;
}
在这个示例中,我们定义了一个类A和一个友元函数add以及B类和B类中的display函数。B类对A类执行了一些操作,这样两个类的耦合性就增加了。这样的代码结构可能导致类之间的依赖过于紧密,从而影响代码的稳定性和可维护性。
总结:
友元是指在一个类中,另外一个类或函数可以直接访问其中被声明为友元的类的私有成员的机制。友元机制可以使得代码更加灵活,在不破坏封装和安全性的前提下,实现不同类之间的信息交流和协作。但是,过多的友元声明会破坏封装和安全性,应该避免滥用。
友元机制包含两种类型:友元函数和友元类。友元函数是一个非成员函数,可以直接访问一个类的私有成员。友元类是一个类,可以访问被它声明为友元的类的私有成员。
需要注意的是,友元关系不能被继承和传递,尽量避免对整个类进行友元声明。友元函数和友元类的声明可以写在类的公有、保护或私有访问说明符下面,但是一般情况下,最好将友元声明写在私有访问说明符下面,以达到封装私有成员的目的。
在实际应用中,友元机制应该被谨慎地使用,只用于必要的情况,并尽可能地将私有成员封装成公有接口。友元机制是C++中重要的特性之一,掌握友元机制可以使我们更好地进行C++编程。