为了让一个类或者函数能够访问某个类里面的私有成员,可以把这个类或函数作为某个类的友元。比如,这里
面把全局函数main作为某个类的友元函数:
#include <iostream>
using namespace std;
struct Test
{
private:
int m_a;
friend int main();
};
int main()
{
Test test;
test.m_a = 90; // 直接访问私有成员 ok
cout << test.m_a << endl;
return 1;
}
为了搞清楚访问权限的问题,我觉得先要建立区间的概念。一个函数是一个区间,一个struct是一个区间,一
个class是一个区间。区间struct或class可以用private和protected来筑起一道墙,以防止别的区间对其区间
进行访问。如果特意为某个区间开放的话,可以把某区间作为自己区间的朋友(friend)即可。
这些墙,在程序编译的时候才起作用。当程序运行时,所有的对象都是一个个存储块,里面的数据没有任何的
private之类的限定,对它的读写都是可能的。
C++是一种实用的语言,我不应该追求完美的封装,应该大胆使用friend和public。
这样写法是没有问题:我是在vc6.0上进行的测试。
#include <iostream>
using namespace std;
struct A;
struct B
{
public:
void f( A ); // 如果编译器从上住下编译的话,它是不知道参数A的大小的,但编译却通过了
};
struct A
{
private:
int m_a;
friend void B::f( A );
};
void B::f( A a )
{
a.m_a = 90;
cout << a.m_a << endl;
}
int main()
{
A a;
B b;
b.f( a ); // ok
return 1;
}
这种写法更是没有问题:
#include <iostream>
using namespace std;
struct A;
struct B
{
public:
void f( A* ); // 编译器不用知道A的定义就知道这里面的参数要占几个字节,因为它是指针变量
};
struct A
{
private:
int m_a;
friend void B::f( A* );
};
void B::f( A* a )
{
a->m_a = 90;
cout << a->m_a << endl;
}
int main()
{
A a;
B b;
b.f( &a ); // ok of course
return 1;
}
句柄思想在生活和开发上经常有体现,它核心就是“以小带大”。在vs2005的类视图中,从一个类名可以看到
一系列的函数,从一个函数名可以看到若干行代码的集合。。。这些都是以小带大的思想的体现。
使用句柄有两个好处:一是可以隐藏使用到的具体数据类型,包括它的声明而不仅是它的实现,避免竞争对手
等“敌人”猜测出你在使用什么技术。二是在这个具体数据类型发生改变时,所有包括这个句柄类头文件的cpp
文件都不需要编译(因为这个句柄类头文件没有改变),而只需要编译这个具体数据类型所在的cpp文件并连接
所有目标文件即可。
下面给出不使用和使用句柄类的例子:
/// 不使用句柄类的情况:
// RealClass.h文件
#ifndef _HANDLE_H
#define _HANDLE_H
struct RealClass // 这个类的定义暴露在头文件中
{
int m_data;
};
#endif
以上没有使用句柄类,除了暴露之外,当RealClass类发生改变时,所有包含这个头文件的cpp文件都要重新编
译。
/// 使用句柄类的情况:
// handle.h文件
#ifndef _HANDLE_H
#define _HANDLE_H
class Handle // 句柄类
{
private:
struct RealClass; // 这个是实际的类,它的任何信息在这个文件中都看不到
RealClass * m_pRealClass;
public:
Handle();
~Handle();
// other interface
};
#endif
// realClass.cpp文件
#include "handle.h"
struct Handle::RealClass // 实际类的定义
{
int m_data;
};
Handle::Handle()
{
m_pRealClass = new RealClass();
}
Handle::~Handle()
{
delete m_pRealClass;
m_pRealClass = 0;
}
以上使用了句柄类,当实际类RealClass的定义发生改变时,由于handle.h文件没有改变,所以包含handle.h的
cpp文件都不需要重新编译。