成员函数 非成员函数 友元函数

原创 2007年10月02日 22:48:00

1.成员函数与非成员函数主要区别是:成员函数可以是虚拟的,而非成员函数则不能。
2.operator>> and operator<< 不能是成员函数,主要是一种调用的习惯的约束

看下面的例子,将operator>> and operator<<设计成成员函数:
// a class that incorrectly declares operator>> and
// operator<< as member functions
class String {
public:
  String(const char *value);
  ...
  istream& operator>>(istream& input);
  ostream& operator<<(ostream& output);
private:
  char *data;
};
String s;
s >> cin;                   // legal, but contrary
                            // to convention
s << cout;                  // ditto
从上可以看出设计成成员函数也是可以的,但调用时将变成XXX >> cin;和XXX << cout;这种奇怪的形式。(c++已经习惯了cin >> XXX;cout << XXX;形式)

正确方法:定义成非成员函数
istream& operator>>(istream& input, String& string)
{
  delete [] string.data;
  read from input into some memory, and make string.data
  point to it
  return input;
}
ostream& operator<<(ostream& output,
                    const String& string)
{
  return output << string.data;
}

3.只有非成员函数可以对其最左边的参数进行类型转换,成员函数不可以。
Only non-member functions get type conversions on their left-most argument

Consider a class for representing rational numbers:

class Rational {
public:
  Rational(int numerator = 0, int denominator = 1);
  int numerator() const;
  int denominator() const;
private:
  ...
};

class Rational {
public:
  ...
  const Rational operator*(const Rational& rhs) const;
};

Rational oneEighth(1, 8);
Rational oneHalf(1, 2);
Rational result = oneHalf * oneEighth;      // fine
result = result * oneEighth;                // fine

result = oneHalf * 2;      // fine
result = 2 * oneHalf;      // error!

上面使用的操作符形式语句与下面使用函数形式语句等价:
 
result = oneHalf.operator*(2);      // fine
result = 2.operator*(oneHalf);      // error!

为什么会这样呢?乘法操作应该是可以互换位置的,所以这种设计有一定的局限性:
它只适用于两个Rational类型的乘法运算。下面我们看一下出现这种情况的原因:
result = oneHalf * 2;通过调用const Rational operator*(const Rational& rhs) const;函数来完成操作,也就是
result = oneHalf.operator*(2); 因为2是int型,而函数原型中的参数为const Rational& rhs,它要的是一个Rational类型。所以必须将2转换成Rational类型。
result = oneHalf * 2;之所以能正确执行,主要是因为编译器帮我们做了隐蔽的类型转换implicit type conversion,它通过Rational的构造函数来完成转换,如下:
const Rational temp(2);      // create a temporary
                             // Rational object from 2
result = oneHalf * temp;     // same as
                             // oneHalf.operator*(temp);

而result = 2 * oneHalf;调用为result = 2.operator*(oneHalf); 可是2并不是Rational对象,它并不能调用成员函数operator*,编译器将试图寻找一个非成员函数operator*,并调用result = operator*(2, oneHalf); 来完成操作,但并没有定义这样的函数,因此将产生错误。

上面的类型转换只有在构造函数为非explicit时进行,如果构造函数为explicit时,编译器不会进行这种隐蔽类型转换
因为explicit构造函数不能用于implicit conversions,下面说明了这种情况:
 
class Rational {
public:
  explicit Rational(int numerator = 0,     // this ctor is
                    int denominator = 1);  // now explicit
  ...
  const Rational operator*(const Rational& rhs) const;
  ...
};
下面两个调用都将产生错误
result = oneHalf * 2;             // error!
result = 2 * oneHalf;             // error!

解决办法:将成员函数改为非成员函数
The solution:
class Rational {
  ...                               // contains no operator*
};

// declare this globally or within a namespace; see
// Item M20 for why it's written as it is
const Rational operator*(const Rational& lhs,
                         const Rational& rhs)
{
  return Rational(lhs.numerator() * rhs.numerator(),
                  lhs.denominator() * rhs.denominator());
}
Rational oneFourth(1, 4);
Rational result;
result = oneFourth * 2;           // fine
result = 2 * oneFourth;           // hooray, it works!

那么是不是应该将operator*作为类Rational的friend呢(友元函数),因为operator*并没有访问类Rational的私有成员,因此没有必要将它作为类Rational的friend。
Whenever you can avoid friend functions, you should, because, much as in real life, friends are often more trouble than they're worth.
当可以不使用友元时,就不要使用,因为它经常带来很多麻烦。

看另外一个使用了友元的例子:
See another example:
class Rational {
public:
  Rational(int numerator = 0, int denominator = 1);
  ...
private:
  int n, d;              // numerator and denominator
friend
  const Rational                      // see Item 21 for why
    operator*(const Rational& lhs,    // the return value is
              const Rational& rhs)    // const
};
inline const Rational operator*(const Rational& lhs,
                          const Rational& rhs)
{
  return Rational(lhs.n * rhs.n, lhs.d * rhs.d);
}

这里为什么将operator*声明成类Rational的friend呢?因为在operator*的实现中访问了类Rational的私有成员。
注意:类的友元函数在类中声明,在类外定义,它不是类的成员函数,不要看到它在类中声明就把它当成员函数用。

4.关于运算符
运算符设计的两种方式:
a.非成员函数形式
non-member function style: ( two parameters )
inline const Rational operator*(const Rational& lhs,
                                const Rational& rhs)
{
  return Rational(lhs.numerator() * rhs.numerator(),
                  lhs.denominator() * rhs.denominator());
}
istream& operator>>(istream& input, String& string)

调用方式:
Rational a,b;
a*b;       //  call  operator*(a,b);
String str;
cin >> str;    //  call  operator>>(cin,str);

b.成员函数形式
member function style::  ( one parameter )
const Rational operator*(const Rational& rhs) const;
istream& operator>>(istream& input);

调用方式:
Rational a,b;
a*b;       //  call  a.operator*(b);
String str;
str >> cin   //   call  str.operator>>(cin); 

关于类中的成员函数和非成员函数(即友元函数)的选择

在定义类的成员函数时,经常遇到运算符重载的情况,wang
  • erpng
  • erpng
  • 2014年05月24日 21:11
  • 669

读书笔记 effective c++ Item 23 宁可使用非成员非友元函数函数也不使用成员函数

1. 非成员非友元好还是成员函数好? 想象一个表示web浏览器的类。这样一个类提供了清除下载缓存,清除URL访问历史,从系统中移除所有cookies等接口:   1 class WebBr...
  • dapangzi88
  • dapangzi88
  • 2017年03月02日 20:55
  • 150

成员函数、友元函数和一般函数的区别-时间

/* *Copyright (c) 2016 *All rights reserved. *文件名称:test.cpp *作 者:史红浩 *完成日期:2016年...
  • jiaowohaohao
  • jiaowohaohao
  • 2016年05月09日 12:13
  • 1926

运算符重载函数作为类成员函数与友元函数的区别

运算符重载函数作为类成员函数与友元函数
  • Sharp_Zjf
  • Sharp_Zjf
  • 2016年03月16日 10:49
  • 1987

C++成员函数,非成员函数,友元函数,隐式类类型转换

博客的所有内容都是个人理解,难免有理解错误的地方,欢迎大家多多拍砖! 成员函数是指在类内部定义的函数,非成员函数是指在某个命名空间或者全局空间内的函数,友元函数是指某些虽然不是类成员却能够访问类的所...
  • zhangchunminggucas
  • zhangchunminggucas
  • 2012年05月17日 15:38
  • 5317

友元(友元函数、友元类和友元成员函数) C++

有些情况下,允许特定的非成员函数访问一个类的私有成员,同时仍阻止一般的访问,这是很方便做到的。例如被重载的操作符,如输入或输出操作符,经常需要访问类的私有数据成员。 友元(frend)机制允许一个类将...
  • u012796139
  • u012796139
  • 2015年06月07日 21:34
  • 3650

C++中友元函数和成员函数的区别

什么是友元函数:     指某些虽然不是类的成员却能够访问类的所有成员的函数 为什么要使用:     为了使其他类的成员函数来访问该类的私有变量 什么时候使用:     可以用于运算符重...
  • dandanzmc
  • dandanzmc
  • 2013年09月13日 21:24
  • 3313

使用成员函数、友元函数和一般函数的区别

#include using namespace std; class Time { public: Time(int h,int m,int s):hour(h),minute(m),se...
  • ListeningForever
  • ListeningForever
  • 2016年05月25日 16:43
  • 1753

类成员函数作为其他类的友元函数的实现(c++语言)

关于类成员函数作为友元函数的编写方法(1) 单文件编译#include using std::cout;class One; //Another中使用One的引用,故前向引用声明class Anoth...
  • dupei
  • dupei
  • 2009年08月12日 13:18
  • 1951

C++参考——成员函数、友元函数和一般函数有区别

返回:贺老师课程教学链接【项目4-成员函数、友元函数和一般函数有区别】(1)阅读下面的程序,体会注释中的说明。//例:使用成员函数、友元函数和一般函数的区别 #include using names...
  • sxhelijian
  • sxhelijian
  • 2015年04月07日 15:38
  • 2645
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:成员函数 非成员函数 友元函数
举报原因:
原因补充:

(最多只允许输入30个字)