c++友元函数的声明声明过后,还是不能提示未声明,如下图,怎么回事啊?

C++友元(友元函数和友元类)
在一个类中可以有公用的(public )成员和私有的(private )成员,在类外可以访问公用成员,只有本类中的函数可以访问本类的私有成员。现在,我们来补充介绍一个例外&&友元(friend )。
fnend的意思是朋友,或者说是好友,与好友的关系显然要比一般人亲密一些。有的 家庭可能会这样处理:客厅对所有来客开放,而卧室除了本家庭的成员可以进人以外,还 允许好朋友进入。在C++中,这种关系以关键宇friend声明,中文多译为友元。友元可以访问与其有好友关系的类中的私有成员,友元包括友元函数和友元 类。如果您对友元这个名词不习惯,可以按原文friend理解为朋友即可。
如果在本类以外的其他地方定义了一个函数(这个函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数),在类体中用friend对其进行声明,此函数就称为本类的友元函数。友元函数可以访问这个类中的私有成员。
1) 将普通函数声明为友元函数
通过下面的例子可以了解友元函数的性质和作用。
[例9.12] 友元函数的简单例子。
#include &iostream&
using namespace std;
class Time
Time(int,int,int);
friend void display(Time &);
int minute;
Time::Time(int h,int m,int s)
void display(Time& t)
cout&&t.hour&&&:&&&t.minute&&&:&&&t.sec&&endl;
int main( )
Time t1(10,13,56);
display(t1);
程序输出结果如下: 10:13:56 请注意display是一个在类外定义的且未用类Time作限定的函数,它是非成员函数,不属于任何类。它的作用是输出时间(时、分、秒)。如果在 Time类的定义体中未声明display函数为friend函数,它是不能引用Time中的私有成员 hour,minute,sec 的。大家可以测试一下,将上面程序中的第6行删去,观察编译时的信息。 现在由于声明了display是Time类的friend函数,所以display函数可以引用Time中的私有成员hour,minute,sec。但注意在引用这些私有数据成员时,必须加上对象名,不能写成 & &cout&&hour&&&P:&P&&minute&&&P:&P&&sec&& 因为display函数不是Time类的成员函数,不能默认引用Time类的数据成员,必须指定要访问的对象。 2) 友元成员函数 friend函数不仅可以是一般函数(非成员函数),而且可以是另一个类中的成员函数。见例9.13。 [例9.13] 友元成员函数的简单应用。在本例中除了介绍有关友元成员函数的简单应用外,还将用到类的提前引用声明,请读者注意。
#include &iostream&
using namespace std;
class Date;
class Time
Time(int,int,int);
void display(Date &);
int minute;
class Date
Date(int,int,int);
friend void Time::display(Date &);
int month;
Time::Time(int h,int m,int s)
void Time::display(Date &d)
cout&&d.month&&&/&&&d.day&&&/&&&d.year&&endl;
cout&&hour&&&:&&&minute&&&:&&&sec&&endl;
Date::Date(int m,int d,int y)
int main( )
Time t1(10,13,56);
Date d1(12,25,2004);
t1.display(d1);
运行时输出: 12/25/2004 (输出Date类对象d1中的私有数据) 10:13:56 (输出Time类对象t1中的私有数据) 在本例中定义了两个类Time和Date。程序第3行是对Date类的声明,因为在第7行和第16行中对display函数的声明和定义中要用到类名 Date,而对Date类的定义却在其后面。能否将Date类的声明提到前面来呢?也不行,因为在Date类中的第4行又用到了Time类,也要求先声明 Time类才能使用它。为了解决这个问题,C++允许对类作&提前引用&的声明,即在正式声明一个类之前,先声明一个类名,表示此类将在稍后声明。程序第 3行就是提前引用声明,它只包含类名,不包括类体。如果没有第3行,程序编译就会出错。 在这里简要介绍有关对象提前引用的知识。在一般情况下,对象必须先声明,然后才能使用它。但是在特殊情况下(如上面例子所示的那样),在正式声明类之前, 需要使用该类名。但是应当注意:类的提前声明的使用范围是有限的。只有在正式声明一个类以后才能用它去定义类对象。如果在上面程序第3行后面增加一行: & & Date d1; //企图定义一个对象 会在编译时出错。因为在定义对象时是要为这些对象分配存储空间的,在正式声明类之前,编译系统无法确定应为对象分配多大的空间。编译系统只有在&见到&类 体后,才能确定应该为对象预留多大的空间。在对一个类作了提前引用声明后,可以用该类的名字去定义指向该类型对象的指针变量或对象的引用变量(如在本例 中,定义了Date类对象的引用变量)。这是因为指针变量和引用变量本身的大小是固定的,与它所指向的类对象的大小无关。 请注意程序是在定义Time::display函数之前正式声明Date类的。如果将对Date类的声明的位置(程序13~21行)改到定义 Time::display函数之后,编译就会出错,因为在Time::display函数体中要用到Date类的成员month,day,year。如 果不事先声明Date类,编译系统无法识别成员month,day,year等成员。 在一般情况下,两个不同的类是互不相干的。在本例中,由于在Date类中声明了Time类中的display成员函数是Date类的&朋友&,因此该函数 可以引用Date类中所有的数据(包括公用的和私有的数据)。如果不声明display成员函数为Date类友元函数的话,Time类中的display 函数只能引用Time类对象中的私有成员,而决不能引用Date类 对象中的私有成员。 请注意在本程序中调用友元函数访问有关类的私有数据方法:
在函数名display的前面要加display所在的对象名(t1);
display成员函数的实参是Date类对象d1,否则就不能访问对象d1中的私有数据;
在Time::display函数中引用Date类私有数据时必须加上对象名,如d.month。
3) 一个函数(包括普通函数和成员函数)可以被多个类声明为&朋友&,这样就可以引用多个类中的私有数据。 例如,可以将例9.13程序中的display函数不放在Time类中,而作为类外的普通函数,然后分别在Time和Date类中将display声明为 朋友。在主函数中调用display函数,display函数分别引用Time和Date两个类的对象的私有数据,输出年、月、日和时、分、秒。
不仅可以将一个函数声明为一个类的&朋友&,而且可以将一个类(例如B类)声明为另一个类(例如A类)的&朋友&。这时B类就是A类的友元类。 友元类B中的所有函数都是A类的友元函数,可以访问A类中的所有成员。在A类的定义体中用以下语句声明B类为其友元类: & & friend B; 声明友元类的一般形式为: & & friend 类名; 关于友元,有两点需要说明:
友元的关系是单向的而不是双向的。如果声明了 B类是A类的友元类,不等于A类是B类的友元类,A类中的成员函数不能访问B类中的私有数据。
友元的关系不能传递,如果B类是A类的友元类,C类是B类的友元类,不等于 C类是A类的友元类。例如,张三的好友是李四,而李四有好友王五,显然,王五不一定是张三的好友。如果想让C类是A类的友元类,应在A类中另外声明。
在实际工作中,除非确有必要,一般并不把整个类声明为友元类,而只将确实有需要的成员函数声明为友元函数,这样更安全一些。 关于友元利弊的分析: 面向对象程序设计的一个基本原则是封装性和信息隐蔽,而友元却可以访问其他类中的私有成员,不能不说这是对封装原则的一个小的破坏。但是它能有助于数据共 享,能提高程序的效率,在使用友元时,要注意到它的副作用,不要过多地使用友元,只有在使用它能使程序精炼,并能大大提高程序的效率时才用友元。也就是 说,要在数据共享和信息隐蔽之间选择一个恰当的平衡点。
相关内容:
编程爱好者
WEB编程开发C++之友元:友元函数和友元类详解
字体:[ ] 类型:转载 时间:
友元是一种允许非类成员函数访问类的非公有成员的一种机制。可以把一个函数指定为类的友元,也可以把整个类指定为另一个类的友元
一、友元介绍我们知道,类的成员函数可以访问同类的其他成员函数,包括公有、私有和保护成员。而类的外部函数只能访问类的公有成员。友元是一种允许非类成员函数访问类的非公有成员的一种机制。可以把一个函数指定为类的友元,也可以把整个类指定为另一个类的友元。
友元函数友元类
二、友元函数友元函数在类作用域外定义,但它需要在类体中进行说明为了与该类的成员函数加以区别,定义的方式是在类中用关键字friend说明该函数,格式如下:
friend& 类型 友元函数名(参数表);友元的作用在于提高程序的运行效率
友元函数注意事项:1、友元函数不是类的成员函数,在函数体中访问对象的成员,必须用对象名加运算符“.”加对象成员名。但友元函数可以访问类中的所有成员(公有的、私有的、保护的),一般函数只能访问类中的公有成员。
2、友元函数不受类中的访问权限关键字限制,可以把它放在类的公有、私有、保护部分,但结果一样。
3、某类的友元函数的作用域并非该类作用域。如果该友元函数是另一类的成员函数,则其作用域为另一类的作用域,否则与一般函数相同。
4、友元函数破坏了面向对象程序设计类的封装性,所以友元函数如不是必须使用,则尽可能少用。或者用其他手段保证封装性。 代码如下:#include &math.h&#include &iostream&class Point{&&& friend double Distance(const Point &p1, const Point &p2);public:&&& Point(int x, int y);private:&&& int x_;&&& int y_;};Point::Point(int x, int y) : x_(x), y_(y){}double Distance(const Point &p1, const Point &p2){&&& double dx = p1.x_ - p2.x_;&&& double dy = p1.y_ - p2.y_;&&& return sqrt(dx * dx + dy * dy);}int main(void){&&& Point p1(3, 4);&&& Point p2(6, 9);&&& cout && Distance(p1, p2) &&&&& return 0;}程序中Distance 是Point类的友元函数,可以访问类的私有数据成员。
三、友元类如果某类B的成员函数会频繁的存取另一个类A的数据成员, 而A的数据成员的Private/Protectd限制造成B存取的麻烦, B只能通过A的Public的成员函数进行间接存取把B做成A类的友元类,即A类向B类开放其Private/Protectd内容, 让B直接存取友元类:一个类可以作另一个类的友元友元类的所有成员函数都是另一个类的友元函数友元类的声明:friend class 类名;
友元类注意事项:1、友元关系是单向的2、友元关系不能被传递3、友元关系不能被继承
TeleController.h : 代码如下:#ifndef& _TELE_CONTROLLER_H_#define _TELE_CONTROLLER_H_class Tclass TeleController{public:&&& void VolumeUp(Television &tv);&&& void VolumeDown(Television &tv);&&& void ChanelUp(Television &tv);&&& void ChanelDown(Television &tv);};#endif // _TELE_CONTROLLER_H_ TeleController.cpp : 代码如下:#include "TeleController.h"#include "Television.h"void TeleController::VolumeUp(Television &tv){&&& tv.volume_ += 1;}void TeleController::VolumeDown(Television &tv){&&& tv.volume_ -= 1;}void TeleController::ChanelUp(Television &tv){&&& tv.chanel_ += 1;}void TeleController::ChanelDown(Television &tv){&&& tv.volume_ -= 1;} Television.h: 代码如下:#ifndef _TELEVISION_H_#define _TELEVISION_H_class TeleCclass Television{&&& friend class TeleCpublic:&&& Television(int volume, int chanel);private:&&& int volume_;&&& int chanel_;};#endif // _TELEVISION_H_Television.cpp: 代码如下:#include "Television.h"Television::Television(int volume, int chanel) : volume_(volume), chanel_(chanel){} main.cpp: 代码如下:#include "Television.h"#include "TeleController.h"#include &iostream&
int main(void){&&& Television tv(1, 1);&&& TeleC&&& tc.VolumeUp(tv);&&& return 0;}
将TeleController 类作为Television类的友元类,这样TeleController 类的成员函数就都可以访问Television类的所有成员,包括私有。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具c/c++(108)
友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend,其格式如下:
friend 类型 函数名(形式参数);
友元提供了不同类的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。通过友元,一个不同函数或另一个类中的成员函数可以访问类中的私有成员和保护成员。c++中的友元为封装隐藏这堵不透明的墙开了一个小孔,外界可以通过这个小孔窥视内部的秘密。
友元的正确使用能提高程序的运行效率,但同时也破坏了类的封装性和数据的隐藏性,导致程序可维护性变差。
友元函数的声明可以放在类的私有部分,也可以放在公有部分,它们是没有区别的,都说明是该类的一个友元函数。
一个函数可以是多个类的友元函数,只需要在各个类中分别声明。
友元函数的调用与一般函数的调用方式和原理一致。
二、友元函数语法
friend 类型名 友元函数名(形参表);
然后在类体外对友元函数进行定义,定义的格式和普通函数相同,但友元函数可以通过对象作为参数直接访问对象的私有成员。
被声明为两个类的友元函数
如果我们决定一个函数必须被声明为两个类的友元则友元声明如下
class W // 只声明
class Screen
friend bool is_equal( Screen &, Window & );
class Window
friend bool is_equal( Screen &, Window & );
作为一个类的函数又是另一个类的友元
如果我们决定该函数必须作为一个类的成员函数并又是另一个类的友元,则成员函数声明和友元声明如下:
class Screen
// copy 是类 Screen 的成员
Screen& copy( Window & );
class Window
// copy 是类 Window 的一个友元
friend Screen& Screen::copy( Window & );
只有当一个类的定义已经被看到时它的成员函数才能被声明为另一个类的友元。这并不总是能够做到的。
例如如果Screen 类必须把Window 类的成员函数声明为友元,而Window类必须把Screen 类的成员函数声明为友元。该怎么办呢?在这种情况下可以把整个Window类声明为Screen 类的友元。
class Screen
friend class W
Screen 类的非公有成员现在可以被Window 的每个成员函数访问。
(1) 友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明
三、使用注意
说明如下:
1)必须在类的说明中说明友元函数,说明时以关键字friend开头,后跟友元函数的函数原型,友元函数的说明可以出现在类的任何地方,包括在private和public部分;
2)注意友元函数不是类的成员函数,所以友元函数的实现和普通函数一样,在实现时不用”::”指示属于哪个类,只有成员函数才使用”::”作用域符号;
3)友元函数不能直接访问类的成员,只能访问对象成员;
4)友元函数可以访问对象的私有成员,但普通函数不行;
5)调用友元函数时,在实际参数中需要指出要访问的对象;
6)类与类之间的友元关系不能继承;
7)一个类的成员函数也可以作为另一个类的友元,但必须先定义这个类。
四、从一个例子说起【什么时候用友元类】
成员函数和非成员函数最大的区别在于成员函数可以是虚拟的而非成员函数不行。
所以,如果有个函数必须进行动态绑定(见条款38),就要采用虚拟函数,而虚拟函数必定是某个类的成员函数。关于这一点就这么简单。如果函数不必是虚拟的,情况就稍微复杂一点。
例如有理数类:
class rational {
rational(int numerator = 0,int denominator = 1);
int numerator() const;
int denominator() const;
这是一个没有一点用处的类。(用条款18的术语来说,接口的确最小,但远不够完整。)所以,要对它增加加,减,乘等算术操作支持,但是,该用成员函数还是非成员函数,或者,非成员的友元函数来实现呢?
当拿不定主意的时候,用面向对象的方法来考虑!有理数的乘法是rational类自己该做的事情,所以,写一个成员函数把这个操作包到类中。
class rational {
const rational operator*(const rational& rhs)
(如果你不明白为什么这个函数以这种方式声明——返回一个const值而取一个const的引用作为它的参数——参考条款21-23。)
条款21: 尽可能使用const
条款22: 尽量用“传引用”而不用“传值”
条款23: 必须返回一个对象时不要试图返回一个引用
可以很容易地对有理数进行乘法操作:
rational oneeighth(1,8);
rational onehalf(1,2);
rational result = onehalf *
result = result *
但不要满足,还要支持混合类型操作,比如,rational要能和int相乘。但当写下下面的代码时,只有一半工作:
result = onehalf * 2;
result = 2 *
result = onehalf.operator*⑵;
result = 2.operator*(onehalf);
对象onehalf是一个包含operator*函数的类的实例,所以编译器调用了那个函数。而整数 2 没有相应的类,所以没有operator*成员函数。编译器还会去搜索一个可以象下面这样调用的非成员的operator*函数(即,在某个可见的名字空间里的operator*函数或全局的operator*函数):
result = operator*(2,onehalf);
但没有这样一个参数为int和rational的非成员operator*函数,所以搜索失败。
再看看那个成功的调用。它的第二参数是整数2,然而rational::operator*期望的参数却是rational对象。怎么回事?为什么2在一个地方可以工作而另一个地方不行?
秘密在于隐式类型转换。编译器知道传的值是int而函数需要的是rational,但它也同时知道调用rational的构造函数将int转换成一个合适的rational,所以才有上面成功的调用(见条款19)。换句话说,编译器处理这个调用时的情形类似下面这样:
const rational temp⑵;
result = onehalf *
当然,只有所涉及的构造函数没有声明为explicit的情况下才会这样,因为explicit构造函数不能用于隐式转换,这正是explicit的含义。如果rational象下面这样定义:
class rational {
explicit rational(int numerator = 0,int denominator = 1); //此构造函数为explicit
const rational operator*(const rational& rhs)
那么,下面的语句都不能通过编译:
result = onehalf * 2;
result = 2 *
这不会为混合运算提供支持,但至少两条语句的行为一致了。
然而,我们刚才研究的这个类是要设计成可以允许固定类型到rational的隐式转换的——这就是为什么rational的构造函数没有声明为explicit的原因。这样,编译器将执行必要的隐式转换使上面 result 的第一个赋值语句通过编译。
实际上,如果需要的话,编译器会对每个函数的每个参数执行这种隐式类型转换。但它只对函数参数表中列出的参数进行转换,决不会对成员函数所在的对象(即,成员函数中的*this指针所对应的对象)进行转换。这就是为什么这个语句可以工作:
result = onehalf.operator*⑵;
而这个语句不行:
result = 2.operator*(onehalf);
第一种情形操作的是列在函数声明中的一个参数,而第二种情形不是。
尽管如此,你可能还是想支持混合型的算术操作,而实现的方法应该清楚了:使operator*成为一个非成员函数,从而允许编译器对所有的参数执行隐式类型转换:
const rational operator*(const rational& lhs,const rational& rhs)
rational onefourth(1,4);
rational result;
result = onefourth * 2;
result = 2 *
这当然是一个完美的结局,但还有一个担心:operator*应该成为rational类的友元吗?
这种情况下,答案是不必要。因为operator*可以完全通过类的公有(public)接口来实现。上面的代码就是这么做的。
这里还说明了一个关键问题:
const Test operator+(const Test&lsh,const Test&rsh);
详见下面的例子:
#include &iostream&
using namespace std
Add(int n):para(n) {}
protected:
Add operator+(Add lsh,Add rsh) {
return lsh.para+rsh.para
int main()
Add c = a+b
cout&&c.para&&endl
上面的例子输出11,说明了这点。
只要能避免使用友元函数就要避免,因为,和现实生活中差不多,友元(朋友)带来的麻烦往往比它(他/她)对你的帮助多。
成员的函数
然而,很多情况下,不是成员的函数从概念上说也可能是类接口的一部分,它们需要访问类的非公有成员的情况也不少。
让我们回头再来看看本书那个主要的例子,string类。如果想重载operator&&;和operator&&;来读写string对象,你会很快发现它们不能是成员函数。如果是成员函数的话,调用它们时就必须把string对象放在它们的左边:
class string {
string(const char *value);
istream& operator&&(istream& input);
ostream& operator&&(ostream& output);
这会把别人弄糊涂。所以这些函数不能是成员函数。注意这种情况和前面的不同。这里的目标是自然的调用语法,前面关心的是隐式类型转换。
正确用法:
istream& operator&&(istream& input,string& string)
delete [] string.
read from input into some memory,and make string.data
point to it
ostream& operator&&(ostream& output, const string& string)
return output && string.
注意上面两个函数都要访问string类的data成员,而这个成员是私有(private)的。但我们已经知道,这个函数一定要是非成员函数。这样,就别无选择了:需要访问非公有成员的非成员函数只能是类的友元函数。本条款得出的结论
假设 f() 是想正确声明的函数,c 是和它相关的类:
(1)虚函数必须是成员函数。如果 f() 必须是虚函数,就让它成为 c 的成员函数。
(2)operator&&;和operator&&;决不能是成员函数。如果f是operator&&;或operator&&;让 f() 成为非成员函数。如果 f() 还需要访问c的非公有成员,让 f() 成为c的友元函数。
(3)只有非成员函数对最左边的参数进行类型转换。如果 f() 需要对最左边的参数进行类型转换,让 f() 成为非成员函数。如果 f() 还需要访问 c 的非公有成员,让 f() 成为 c 的友元函数。
(4)其它情况下都声明为成员函数。如果以上情况都不是,让f成为c的成员函数。
[1] 百度百科
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:431503次
积分:8993
积分:8993
排名:第1622名
原创:372篇
转载:315篇
评论:149条
文章:20篇
阅读:20252
(3)(5)(11)(7)(6)(1)(172)(26)(3)(18)(59)(3)(11)(2)(4)(14)(12)(45)(4)(12)(8)(1)(1)(1)(4)(15)(89)(45)(1)(4)(7)(14)(58)(3)(1)(13)(11)(3)以下试题来自:
单项选择题下面关于友元的说法中错误的是(
)。A.友元函数可以访问类中的所有数据成员B.友元函数不可以在类内部定义C.友元类的所有成员函数都是另一个类友元函数D.友元函数必须声明在public区
为您推荐的考试题库
你可能感兴趣的试题
1A.仅ⅠB.仅Ⅰ、ⅡC.都是D.都不是2A.8B.16C.32D.153A.多态B.继承C.类D.过程调用4A.队列B.线性表C.二叉树D.栈5A.111B.123C.222D.333
热门相关试卷
最新相关试卷

我要回帖

更多关于 友元类的声明 的文章

 

随机推荐