我一直在阅读c++常见问题,对朋友声明很好奇。我个人从未使用过它,但我对探索这门语言很感兴趣。
使用friend的一个好例子是什么?
读了一些常见问题,我喜欢<< >>操作符重载和添加作为这些类的朋友的想法。然而,我不确定这如何不会破坏封装。什么时候这些异常可以保持在OOP的严格范围内?
我一直在阅读c++常见问题,对朋友声明很好奇。我个人从未使用过它,但我对探索这门语言很感兴趣。
使用friend的一个好例子是什么?
读了一些常见问题,我喜欢<< >>操作符重载和添加作为这些类的朋友的想法。然而,我不确定这如何不会破坏封装。什么时候这些异常可以保持在OOP的严格范围内?
当前回答
另一种用法:friend(+虚拟继承)可以用来避免从一个类派生(又名:“make a class underivable”)=> 1,2
从2:
class Fred;
class FredBase {
private:
friend class Fred;
FredBase() { }
};
class Fred : private virtual FredBase {
public:
...
};
其他回答
当不同的类(不是从另一个类继承一个类)正在使用另一个类的私有或受保护成员时,可以使用友情。
友元函数的典型用例是 在访问私有或受保护的两个不同的类之间进行 两者的成员。
来自http://www.cplusplus.com/doc/tutorial/inheritance/。
在这个例子中,非成员方法访问类的私有成员。这个方法必须在这个类中声明为类的友元。
// friend functions
#include <iostream>
using namespace std;
class Rectangle {
int width, height;
public:
Rectangle() {}
Rectangle (int x, int y) : width(x), height(y) {}
int area() {return width * height;}
friend Rectangle duplicate (const Rectangle&);
};
Rectangle duplicate (const Rectangle& param)
{
Rectangle res;
res.width = param.width*2;
res.height = param.height*2;
return res;
}
int main () {
Rectangle foo;
Rectangle bar (2,3);
foo = duplicate (bar);
cout << foo.area() << '\n';
return 0;
}
好友对于回调也很有用。可以将回调函数作为静态方法来实现
class MyFoo
{
private:
static void callback(void * data, void * clientData);
void localCallback();
...
};
回调在内部调用localCallback, clientData中有你的实例。在我看来,
还是……
class MyFoo
{
friend void callback(void * data, void * callData);
void localCallback();
}
这允许友元在cpp中被定义为c风格的函数,而不会使类变得混乱。
类似地,我经常看到的一种模式是将一个类的所有真正的私有成员放到另一个类中,该类在头文件中声明,在cpp中定义,并加为好友。这允许编码器向头文件的用户隐藏类的很多复杂性和内部工作。
在头文件中:
class MyFooPrivate;
class MyFoo
{
friend class MyFooPrivate;
public:
MyFoo();
// Public stuff
private:
MyFooPrivate _private;
// Other private members as needed
};
在cpp中,
class MyFooPrivate
{
public:
MyFoo *owner;
// Your complexity here
};
MyFoo::MyFoo()
{
this->_private->owner = this;
}
这样就更容易隐藏下游不需要看到的东西。
可能我从上面的答案中漏掉了一些东西,但是封装中另一个重要的概念是隐藏实现。减少对私有数据成员(类的实现细节)的访问,可以更容易地修改代码。如果朋友直接访问私有数据,对实现数据字段(私有数据)的任何更改都会破坏访问该数据的代码。使用访问方法可以很大程度上消除这种情况。我认为相当重要。
这可能不是一个实际的用例情况,但可能有助于说明类间朋友关系的使用。
会所
class ClubHouse {
public:
friend class VIPMember; // VIP Members Have Full Access To Class
private:
unsigned nonMembers_;
unsigned paidMembers_;
unsigned vipMembers;
std::vector<Member> members_;
public:
ClubHouse() : nonMembers_(0), paidMembers_(0), vipMembers(0) {}
addMember( const Member& member ) { // ...code }
void updateMembership( unsigned memberID, Member::MembershipType type ) { // ...code }
Amenity getAmenity( unsigned memberID ) { // ...code }
protected:
void joinVIPEvent( unsigned memberID ) { // ...code }
}; // ClubHouse
会员班的
class Member {
public:
enum MemberShipType {
NON_MEMBER_PAID_EVENT, // Single Event Paid (At Door)
PAID_MEMBERSHIP, // Monthly - Yearly Subscription
VIP_MEMBERSHIP, // Highest Possible Membership
}; // MemberShipType
protected:
MemberShipType type_;
unsigned id_;
Amenity amenity_;
public:
Member( unsigned id, MemberShipType type ) : id_(id), type_(type) {}
virtual ~Member(){}
unsigned getId() const { return id_; }
MemberShipType getType() const { return type_; }
virtual void getAmenityFromClubHouse() = 0
};
class NonMember : public Member {
public:
explicit NonMember( unsigned id ) : Member( id, MemberShipType::NON_MEMBER_PAID_EVENT ) {}
void getAmenityFromClubHouse() override {
Amenity = ClubHouse::getAmenity( this->id_ );
}
};
class PaidMember : public Member {
public:
explicit PaidMember( unsigned id ) : Member( id, MemberShipType::PAID_MEMBERSHIP ) {}
void getAmenityFromClubHouse() override {
Amenity = ClubHouse::getAmenity( this->id_ );
}
};
class VIPMember : public Member {
public:
friend class ClubHouse;
public:
explicit VIPMember( unsigned id ) : Member( id, MemberShipType::VIP_MEMBERSHIP ) {}
void getAmenityFromClubHouse() override {
Amenity = ClubHouse::getAmenity( this->id_ );
}
void attendVIPEvent() {
ClubHouse::joinVIPEvent( this->id );
}
};
设施
class Amenity{};
如果你看看这些类之间的关系;会所拥有各种不同类型的会员资格和会员资格。成员都派生自超类或基类,因为它们都共享公共的ID和枚举类型,外部类可以通过基类中的访问函数访问它们的ID和类型。
然而,通过这种成员及其派生类的层次结构以及它们与ClubHouse类的关系,派生类中唯一具有“特殊特权”的是VIPMember类。基类和其他2个派生类不能访问ClubHouse的joinVIPEvent()方法,但VIP Member类拥有该特权,就好像它拥有对该事件的完全访问一样。
所以对于vip会员和ClubHouse,这是一个双向通道,而其他会员职业是有限的。
在为类实现树算法时,教授给我们的框架代码将树类作为节点类的朋友。
它实际上没有任何好处,除了让你在不使用设置函数的情况下访问成员变量。