友元函数和友元类
类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
为什么要使用友元函数
运算符重载的某些场合需要使用友元。(略)
两个类要共享数据:允许外面的类或函数去访问类的私有变量和保护变量,从而使两个类共享同一函数。
友元函数的优缺点
优点:方便
缺点:友元函数破环了封装机制。不得已的情况下才使用友元函数
友元函数
class A
{
public:
A(int _a):a(_a){};
A() = default;
friend int getA_a(A &_classA);//友元函数
friend std::ostream& operator <<(std::ostream &os,const A& a);
friend std::istream& operator >>(std::istream &in,A& a);
int getA_a(){
std::cout <<"调用类成员函数"<< std::endl;
return this->a;
}
private:
int a;
};
int getA_a(A &_classA){
std::cout <<"调用友元函数"<< std::endl;
return _classA.a;//通过对象名访问私有变量
}
std::ostream& operator <<(std::ostream &os,const A& a){
os << a.a;
return os;
}
std::istream& operator >>(std::istream& in,A& a){
in >> a.a;
return in;
}
友元类
// 友元类主要为了提高效率,同时避免public 过于开放
class C;
class B{
public:
B(int _b):b(_b){}
void set(int v){
b += v / 3 + v / 4;
}
friend class C;
void set_c(std::shared_ptr<C> c){
c_ptrl = c;
}
private:
int b;
std::shared_ptr<class C> c_ptrl;
};
class C{
public:
void set_c(B b){
this->c = 2 * get_b(b);
}
void get_bc(B b){
std::cout <<get_b(b) <<" "<<this->c<< std::endl;
}
private:
int c;
int get_b(B b){
return b.b;
}
};
测试
int main(int argc, char* argv[])
{
// test friend function
A _classA(3);
std::cout << getA_a(_classA)<<std::endl;;//友元函数只是普通函数,可以在任意地方调用
std::cout << _classA.getA_a() << std::endl;
A a;
std::cin >> a;
std::cout << a << std::endl;
// test friend class
B b{0};
std::shared_ptr<C> c(new C{});
b.set_c(c);
using namespace std::chrono_literals;
auto RUN_B = [](B *b){
for(int i = 0; i < 10000; i++){
b->set(i);
std::this_thread::sleep_for(10ms);
}
};
std::thread t1(RUN_B,&b);
auto RUN_C = [](std::shared_ptr<C> c,B &b){
int i = 0;
while(true){
c->set_c(b);
std::cout <<i++<<" : ";c->get_bc(b);
std::this_thread::sleep_for(1s);
}
};
std::thread t2(RUN_C,c,std::ref(b));
t1.join();
t2.join();
return 0;
}
因为友元函数没有this指针,则参数要有三种情况:
要访问非static成员时,需要对象做参数
要访问static成员或全局变量时,则不需要对象做参数
如果做参数的对象是全局对象,则不需要对象做参数
友元函数的位置
因为友元函数是类外的函数,所以它的声明可以放在类的私有段或公有段且没有区别。
友元函数的调用
直接调用,不需要通过对象或指针
友元类个人认为主要在结合Pimp使用