C++ FAQ Lite[14]--友元(更新)

[14] 友元
(Part of C++ FAQ Lite, Copyright © 1991-2001, Marshall Cline, cline@parashift.com)

简体中文版翻译:申旻nicrosoft@sunistudio.com东日制作室东日文档


FAQs in section [14]:


[14.1] 什么是友元(friend)?

允许另一个类或函数访问你的类的东西。

友元可以是函数或者是其他的类。类授给它的友元特别的访问权。通常同一个开发者会在技术和非技术上控制类的友元和成员函数(否则当你想更新你的类时,还要征得其它部分的拥有者的同意)。

TopBottomPrevious sectionNext section ]


[14.2] 友元破坏了封装吗?UPDATED!

[Recently made a bit more emphatic (on 4/01). Click here to go to the next FAQ in the "chain" of recent changes .]

如果被适当的使用,实际上可以增强封装。

当一个类的两部分会有不同数量的实例或者不同的生命周期时,你经常需要将一个类分割成两部分。在这些情况下,两部分通常需要直接存取彼此的数据(这两部分原来在同一个类中,所以你不必增加直接存取一个数据结构的代码;你只要将代码改为两个类就行了)。实现这种情况的最安全途径就是使这两部分成为彼此的友元。

如果你象刚才所描述的那样使用友元,就可以使私有的(private)保持私有。不理解这些的人在以上这种情形下还天真的想避免使用友元,他们要么使用公有的(public)数据(罕见!),要么通过公有的 get()set()成员函数使两部分可以访问数据。而他们实际上破坏了封装。只有当在类外(从用户的角度)看待私有数据仍“有意义”时,为私有数据设置公有的get()set()成员函数才是合理的。在许多情况下,这些 get()/set()成员函数和公有数据一样差劲:它们仅仅隐藏了私有数据的名称,而没有隐藏私有数据本身。

同样,如果你将友元函数当做一种类的public:存取函数的语法不同的变种来使用的话,友元函数就和破坏封装的成员函数一样会破坏封装。换一种说法,类的友元不会破坏封装的壁垒:和类的成员函数一样,它们就是封装的壁垒。

TopBottomPrevious sectionNext section ]


[14.3] 使用友元函数的优缺点是什么?

友元函数在接口设计选择上提供了一定程度的自由。

成员函数和友元函数具有同等的特权(100% 的)。主要的不同在于友元函数象f(x)这样调用,而成员函数象 x.f()这样调用。因此,可以在成员函数(x.f())和友元函数(f(x))之间选择的能力允许设计者选择他所认为更具可读性的语法来降低维护成本。

友元函数主要缺点是需要额外的代码来支持动态绑定时。要得到虚友元(virtual friend)的效果,友元函数应该调用一个隐藏的(通常是 protected:成员函数。这称为虚友元函数用法(Virtual Friend Function Idiom)。例如:

 class Base {
 public:
   friend void f(Base& b);
   
// ...
 protected:
   virtual void do_f();
   
// ...
 };
 
 inline void f(Base& b)
 {
   b.do_f();
 }
 
 class Derived : public Base {
 public:
   
// ...
 protected:
   virtual void do_f();  
// "覆盖" f(Base& b)的行为
   
// ...
 };
 
 void userCode(Base& b)
 {
   f(b);
 }

userCode(Base&)中的f(b)语句将调用虚拟的  b.do_f()。这意味着如果b实际是一个派生类的对象,那么Derived::do_f()将获得控制权。注意派生类覆盖的是保护的虚(protected: virtual)成员函数 do_f(); 而不是它友元函数f(Base&)

TopBottomPrevious sectionNext section ]


[14.4] “友元关系既不继承,也不传递”是什么意思?UPDATED!

[Recently added the "not reciprocal" item thanks to Karel Roose (on 4/01). Click here to go to the next FAQ in the "chain" of recent changes .]

仅仅因为我承认对你的友情,允许你访问我,并不自动地允许你的孩子访问我,并不自动地允许你的朋友访问我,并不自动地允许我访问你。

  • 我不必信任我朋友的孩子。友元的特权不被继承。友元的派生类不一定是友元。如果 Fred 类声明Base类是友元,那么Base类的派生类不会自动地被赋予对于Fred的对象的访问特权。
  • 我不必信任我朋友的朋友。友元的特权不被传递。友元的友元不一定是友元。如果Fred类声明Wilma类是友元,并且Wilma类声明Betty类是友元,那么Betty类不会自动地被赋予对于Fred的对象的访问特权。
  • 你不必仅仅因为我声称你是我的朋友就信任我。友元的特权不是自反的。如果Fred类声明Wilma类是友元,则Wilma对象拥有访问Fred对象的特权,但Fred对象不会自动地拥有对Wilma对象的访问特权。

TopBottomPrevious sectionNext section ]


[14.5] 我的类应该使用成员函数还是友元函数?

尽量使用成员函数,不得已时使用友元。

有时在语法上,友元更好(例如,Fred类中,友元函数允许Fred参数作为第二个参数,而成员函数必须是第一个)。另一个好的用法是二元中缀运算符。例如,如果你想允许aFloat + aComplex 的话,aComplex + aComplex 应该被定义为友元而不是成员函数。(成员函数不允许提升左边的参数,因为那样会改变成员函数调用对象的类)。

在其他情况下,首选成员函数。

TopBottomPrevious sectionNext section ]


E-Mail E-mail the author
C++ FAQ LiteTable of contentsSubject indexAbout the author©Download your own copy ]
Revised Apr 8, 2001

阅读终点,创作起航,您可以撰写心得或摘录文章要点写篇博文。去创作
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++中的字符串重载通常是指对字符串类的运算符进行重载,使得字符串对象可以像基本数据类型一样进行运算。例如,可以对字符串的加法运算符进行重载,使得两个字符串对象可以通过“+”号进行拼接。 下面是一个简单的字符串类的定义和加法运算符重载的示例代码: ```cpp #include <cstring> #include <iostream> class String { public: String() : data(nullptr), length(0) {} String(const char* s) : data(nullptr), length(0) { if (s != nullptr) { length = strlen(s); data = new char[length + 1]; strcpy(data, s); } } ~String() { if (data != nullptr) { delete[] data; } } String operator+(const String& other) const { String result; result.length = length + other.length; result.data = new char[result.length + 1]; strcpy(result.data, data); strcat(result.data, other.data); return result; } friend std::ostream& operator<<(std::ostream& os, const String& str) { os << str.data; return os; } private: char* data; size_t length; }; int main() { String s1("Hello"), s2("world"); String s3 = s1 + s2; std::cout << s3 << std::endl; return 0; } ``` 在上面的代码中,我们定义了一个简单的字符串类`String`,它包含了一个字符数组`data`和字符串的长度`length`。我们对加法运算符进行了重载,使得两个字符串对象可以通过“+”号进行拼接,并返回一个新的字符串对象。在重载函数中,我们首先创建一个新的字符串对象`result`,然后计算拼接后的长度,为`result`分配足够的内存,将两个原始字符串拷贝到`result`中,并返回`result`对象。 最后,我们定义了一个元函数`operator<<`,用于输出字符串对象的内容。 需要注意的是,在实现字符串重载时,需要考虑到内存的分配和释放问题,避免内存泄漏和悬空指针等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Nicrosoft

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值