我一直在阅读c++常见问题,对朋友声明很好奇。我个人从未使用过它,但我对探索这门语言很感兴趣。
使用friend的一个好例子是什么?
读了一些常见问题,我喜欢<< >>操作符重载和添加作为这些类的朋友的想法。然而,我不确定这如何不会破坏封装。什么时候这些异常可以保持在OOP的严格范围内?
我一直在阅读c++常见问题,对朋友声明很好奇。我个人从未使用过它,但我对探索这门语言很感兴趣。
使用friend的一个好例子是什么?
读了一些常见问题,我喜欢<< >>操作符重载和添加作为这些类的朋友的想法。然而,我不确定这如何不会破坏封装。什么时候这些异常可以保持在OOP的严格范围内?
当前回答
这可能不是一个实际的用例情况,但可能有助于说明类间朋友关系的使用。
会所
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,这是一个双向通道,而其他会员职业是有限的。
其他回答
可能我从上面的答案中漏掉了一些东西,但是封装中另一个重要的概念是隐藏实现。减少对私有数据成员(类的实现细节)的访问,可以更容易地修改代码。如果朋友直接访问私有数据,对实现数据字段(私有数据)的任何更改都会破坏访问该数据的代码。使用访问方法可以很大程度上消除这种情况。我认为相当重要。
典型的例子是重载操作符<<。另一个常见的用法是允许助手或管理类访问您的内部。
下面是我从c++朋友那里听到的一些指导原则。最后一个尤其令人难忘。
你的朋友不是你孩子的朋友。 你孩子的朋友并不是你的朋友。 只有朋友才能碰你的隐私部位。
In C++ "friend" keyword is useful in Operator overloading and Making Bridge. 1.) Friend keyword in operator overloading :Example for operator overloading is: Let say we have a class "Point" that has two float variable"x"(for x-coordinate) and "y"(for y-coordinate). Now we have to overload "<<"(extraction operator) such that if we call "cout << pointobj" then it will print x and y coordinate (where pointobj is an object of class Point). To do this we have two option: 1.Overload "operator <<()" function in "ostream" class. 2.Overload "operator<<()" function in "Point" class. Now First option is not good because if we need to overload again this operator for some different class then we have to again make change in "ostream" class. That's why second is best option. Now compiler can call "operator <<()" function:
1.Using ostream object cout.As: cout.operator<<(Pointobj) (form ostream class).
2.Call without an object.As: operator<<(cout, Pointobj) (from Point class).
Beacause we have implemented overloading in Point class. So to call this function without an object we have to add"friend" keyword because we can call a friend function without an object. Now function declaration will be As: "friend ostream &operator<<(ostream &cout, Point &pointobj);" 2.) Friend keyword in making bridge : Suppose we have to make a function in which we have to access private member of two or more classes ( generally termed as "bridge" ) . How to do this: To access private member of a class it should be member of that class. Now to access private member of other class every class should declare that function as a friend function. For example : Suppose there are two class A and B. A function "funcBridge()" want to access private member of both classes. Then both class should declare "funcBridge()" as: friend return_type funcBridge(A &a_obj, B & b_obj);I think this would help to understand friend keyword.
这可能不是一个实际的用例情况,但可能有助于说明类间朋友关系的使用。
会所
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,这是一个双向通道,而其他会员职业是有限的。
你必须非常小心何时何地使用friend关键字,和你一样,我很少使用它。下面是一些关于使用friend和替代用法的注意事项。
假设你想比较两个对象,看它们是否相等。你可以:
使用访问器方法进行比较(检查每个ivar并确定是否相等)。 或者,您可以通过将所有成员设为public直接访问它们。
第一个选项的问题是,这可能会有很多访问器,这比直接变量访问(稍微)慢,更难读取,而且很麻烦。第二种方法的问题是完全破坏了封装。
如果我们可以定义一个外部函数,它仍然可以访问类的私有成员,那就更好了。我们可以用friend关键字做到这一点:
class Beer {
public:
friend bool equal(Beer a, Beer b);
private:
// ...
};
equal(Beer, Beer)方法现在可以直接访问a和b的私有成员(可能是char *brand, float percentAlcohol等)。这是一个相当做作的例子,您可能会更早地将friend应用于重载的==操作符,但我们会讲到这一点。
有几件事需要注意:
A friend is NOT a member function of the class It is an ordinary function with special access to the private members of the class Don't replace all accessors and mutators with friends (you may as well make everything public!) Friendship isn't reciprocal Friendship isn't transitive Friendship isn't inherited Or, as the C++ FAQ explains: "Just because I grant you friendship access to me doesn't automatically grant your kids access to me, doesn't automatically grant your friends access to me, and doesn't automatically grant me access to you."
我只有在用另一种方法更难的时候才会用朋友。另一个例子是,由于Mat2x2, Mat3x3, Mat4x4, Vec2, Vec3, Vec4等的互操作性,许多向量数学函数经常被创建为朋友。而且做朋友比到处使用访问器要容易得多。正如所指出的,friend在应用于<<(非常方便调试)、>>和==运算符时通常很有用,但也可以用于这样的事情:
class Birds {
public:
friend Birds operator +(Birds, Birds);
private:
int numberInFlock;
};
Birds operator +(Birds b1, Birds b2) {
Birds temp;
temp.numberInFlock = b1.numberInFlock + b2.numberInFlock;
return temp;
}
就像我说的,我不经常使用friend这个词,但偶尔它正是你所需要的。希望这能有所帮助!