友元声明只能出现在类定义内部,在类中的位置不限。由于友元非给予访问权限的类的成员,因此不受public
, protected
, private
关键字的制约。[一般而言,友元最好放在类定义的开头,或结尾处]
友元可以是普通函数,可以是另一个类的成员函数,或者是整个类。
友元类
如果一个类成为友元类,那么此类的所有成员函数都将成为成员函数。
友元类的最一般的写法:
class B;
class A
{
// class B 是 class A 的友元类
friend class B;
//... .. ...
}
class B
{
//... .. ...
}
例 1,C++ Primer 上的例子:
class Screen {
// Window_Mgr members can access private parts of class Screen
friend class Window_Mgr;
public:
typedef std::string::size_type index;
// return character at the cursor or at a given position
char get() const { return contents[cursor]; }
char get(index ht, index wd) const;
// remaining members
private:
std::string contents;
index cursor;
index height, width;
};
以上, Window_Mgr
是Screen
的友元,Window_Mgr
的成员函数因此能直接访问Screen
类的私有成员,例如,Window_Mgr
可以有如下的重定位函数:
Window_Mgr& Window_Mgr::relocate(Screen::index r, Screen::index c, Screen& s)
{
// ok to refer to height and width
s.height += r;
s.width += c;
return *this;
}
例 2, MSDN上的友元类的例子:
// classes_as_friends2.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
class YourClass {
friend class YourOtherClass; // Declare a friend class
public:
YourClass() : topSecret(0){}
void printMember() { cout << topSecret << endl; }
private:
int topSecret;
};
class YourOtherClass {
public:
void change( YourClass& yc, int x ){yc.topSecret = x;}
};
int main() {
YourClass yc1;
YourOtherClass yoc1;
yc1.printMember();
yoc1.change( yc1, 5 );
yc1.printMember();
}
例 3, 同时包含友元函数和友元类:
class Rectangle
{
public:
//constructor, destructor and all that jazz
friend class Puzzle;
friend float CalculateArea();
private:
//rectangle's private data
};
class Puzzle
{
//normal class stuff
};
float CalculateArea()
{
//do some fancy calculations
//return result
}
例 4:
#include <iostream>
using namespace std;
class Square;
class Rectangle {
int width, height;
public:
Rectangle(int w = 1, int h = 1):width(w),height(h){}
void display() {
cout << "Rectangle: " << width * height << endl;
};
void morph(Square &);
};
class Square {
int side;
public:
Square(int s = 1):side(s){}
void display() {
cout << "Square: " << side * side << endl;
};
friend class Rectangle;
};
void Rectangle::morph(Square &s) {
width = s.side;
height = s.side;
}
int main () {
Rectangle rec(5,10);
Square sq(5);
cout << "Before:" << endl;
rec.display();
sq.display();
rec.morph(sq);
cout << "\nAfter:" << endl;
rec.display();
sq.display();
return 0;
}
友元函数
一般函数为友元,例 1
这个例子看似相当简单,比C++ Primer上的描述简单多了:
/* C++ program to demonstrate the working of friend function.*/
#include <iostream>
using namespace std;
class Distance
{
private:
int meter;
public:
Distance(): meter(0) { }
//friend function
friend int addFive(Distance); // 友元函数
};
// friend function definition
int addFive(Distance d)
{
//accessing private data from non-member function
d.meter += 5;
return d.meter;
}
int main()
{
Distance D;
cout<<"Distance: "<< addFive(D);
return 0;
}
输出:
Distance: 5
这个例子实际用处不大,当需要同时操作两个不同类的对象时,友元函数就非常有用。当然,不用友元函数也是可以实现的,但代码会比较长,复杂,难以理解。
一般函数为友元,例 2
将两个不同类的成员相加,这个例子比较有意义:
#include <iostream>
using namespace std;
// forward declaration
class B;
class A {
private:
int numA;
public:
A(): numA(12) { }
// 友元函数声明
friend int add(A, B);
};
class B {
private:
int numB;
public:
B(): numB(1) { }
// 友元函数声明
friend int add(A , B);
};
// Function add() is the friend function of classes A and B
// that accesses the member variables numA and numB
int add(A objectA, B objectB)
{
return (objectA.numA + objectB.numB);
}
int main()
{
A objectA;
B objectB;
cout<<"Sum: "<< add(objectA, objectB);
return 0;
}
输出:
Sum: 13
一般函数为友元,例 3
MSDN上的:
// friend_functions.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
class Point
{
friend void ChangePrivate( Point & );
public:
Point( void ) : m_i(0) {}
void PrintPrivate( void ){cout << m_i << endl; }
private:
int m_i;
};
void ChangePrivate ( Point &i ) { i.m_i++; }
int main()
{
Point sPoint;
sPoint.PrintPrivate();
ChangePrivate(sPoint);
sPoint.PrintPrivate();
}
输出:
0
1
一般函数为友元,例 4
cpluscplus forum上的例子, 包含操作符重载:
#include <iostream>
using namespace std;
class Number
{
friend void Reset(Number &t);
private:
int num;
int twiceNum;
public:
Number(int n = 0);
Number(const Number & n);
void setNum(int n);
void setTwiceNum(int tn);
int getNum() const;
int getTwiceNum() const;
Number operator+ (const Number & r);
Number operator++ ();
Number operator++ (int);
};
Number::Number(int n = 0)
{
setNum(n);
}
Number::Number(const Number & n)
{
setNum(num = 0 & twiceNum);
}
void Number::setNum(int n)
{
n = num;
twiceNum = 2 * num;
}
void Number::setTwiceNum(int tn)
{
if (tn % 2 == 0)
{
tn = twiceNum;
}
else
{
twiceNum = 0;
}
num = twiceNum / 2;
}
int Number::getNum() const
{
return num;
}
int Number::getTwiceNum() const
{
return twiceNum;
}
Number Number::operator+ (const Number & r)
{
/*r;
setNum;
return 0;*/
}
Number Number::operator++ ()
{
/*num = num += 1;
setNum;*/
}
Number Number::operator++ (int)
{
/*num = num + 1;
setNum;*/
}
friend void Number::Reset(Number &t) //friend
{
Number::num = 0;
Number::twiceNum = 0;
}
int main()
{
Number a, b(5), c(10);
Number d=c;
Number e;
cout<<"a: " <<a.getNum() << ", 2 X a: "<<a.getTwiceNum()<<endl;
cout<<"b: " <<b.getNum() << ", 2 X b: "<<b.getTwiceNum()<<endl;
cout<<"c: " <<c.getNum() << ", 2 X c: "<<c.getTwiceNum()<<endl;
cout<<"d: " <<d.getNum() << ", 2 X d: "<<d.getTwiceNum()<<endl;
cout <<endl;
Reset(b);
cout <<"After reset of b"<<endl;
cout<<"b: " <<b.getNum() << ", 2 X b: "<<b.getTwiceNum()<<endl;
cout <<endl;
b.setNum(7);
cout <<"After b.setNum(7)"<<endl;
cout<<"b: " <<b.getNum() << ", 2 X b: "<<b.getTwiceNum()<<endl;
cout <<endl;
b.setTwiceNum(21);
cout <<"After b.setTwiceNum(21)"<<endl;
cout<<"b: " <<b.getNum() << ", 2 X b: "<<b.getTwiceNum()<<endl;
cout <<endl;
b.setTwiceNum(30);
cout <<"After b.setTwiceNum(30)"<<endl;
cout<<"b: " <<b.getNum() << ", 2 X b: "<<b.getTwiceNum()<<endl;
cout <<endl;
a= b + c;
cout <<"After a= b+c"<<endl;
cout<<"a: " <<a.getNum() << ", 2 X a: "<<a.getTwiceNum()<<endl;
cout<<"b: " <<b.getNum() << ", 2 X b: "<<b.getTwiceNum()<<endl;
cout<<"c: " <<c.getNum() << ", 2 X c: "<<c.getTwiceNum()<<endl;
cout <<endl;
e= ++a;
cout <<"After e= ++a"<<endl;
cout<<"a: " <<a.getNum() << ", 2 X a: "<<a.getTwiceNum()<<endl;
cout<<"e: " <<e.getNum() << ", 2 X e: "<<e.getTwiceNum()<<endl;
cout <<endl;
e= a++;
cout <<"After e= a++"<<endl;
cout<<"a: " <<a.getNum() << ", 2 X a: "<<a.getTwiceNum()<<endl;
cout<<"e: " <<e.getNum() << ", 2 X e: "<<e.getTwiceNum()<<endl;
cout <<endl;
return 0;
}
一般函数为友元,例 5
#include <iostream>
using namespace std;
class Rectangle {
int width, height;
public:
Rectangle(int w = 1, int h = 1):width(w),height(h){}
friend void display(Rectangle &);
};
void display(Rectangle &r) {
cout << r.width * r.height << endl;
}
int main () {
Rectangle rect(5,10);
display(rect);
return 0;
}
一般函数为友元,例 6
#include <iostream>
using namespace std;
class Square; // forward declaration
class Rectangle {
int width, height;
public:
Rectangle(int w = 1, int h = 1):width(w),height(h){}
friend void display(Rectangle &, Square &);
};
class Square {
int side;
public:
Square(int s = 1):side(s){}
friend void display(Rectangle &, Square &);
};
void display(Rectangle &r, Square &s) {
cout << "Rectangle: " << r.width * r.height << endl;
cout << "Square: " << s.side * s.side << endl;
}
int main () {
Rectangle rec(5,10);
Square sq(5);
display(rec,sq);
return 0;
}
输出:
Rectangle: 50
Square: 25
类成员函数声明为友元:
声明成员函数为友元时,函数名必须用其所属的类名修饰:
class Screen {
// Window_Mgrmust be defined before class Screen
friend Window_Mgr& Window_Mgr::relocate(Window_Mgr::index, Window_Mgr::index, Screen&);
// ...restofthe Screen class
};
例子:
// classes_as_friends1.cpp
// compile with: /c
class B;
class A {
public:
int Func1( B& b );
private:
int Func2( B& b );
};
class B {
private:
int _b;
// A::Func1 is a friend function to class B
// so A::Func1 has access to all members of B
friend int A::Func1( B& );
};
int A::Func1( B& b ) { return b._b; } // OK
int A::Func2( B& b ) { return b._b; } // C2248
[1] C++ Primer 12.5
[2] C++ friend Function and friend Classes (百度快照)
[3] https://msdn.microsoft.com/zh-cn/magazine/h2x4fzdz(v=vs.110)
[4] http://www.cplusplus.com/forum/general/86722/
[5] http://www.bogotobogo.com/cplusplus/friendclass.php