我一直在阅读c++常见问题,对朋友声明很好奇。我个人从未使用过它,但我对探索这门语言很感兴趣。

使用friend的一个好例子是什么?


读了一些常见问题,我喜欢<< >>操作符重载和添加作为这些类的朋友的想法。然而,我不确定这如何不会破坏封装。什么时候这些异常可以保持在OOP的严格范围内?


当前回答

我发现了一个使用好友访问的方便地方:私有函数的Unittest。

其他回答

在做TDD的时候,我经常使用c++中的'friend'关键字。

朋友能知道我的一切吗?


更新:我从Bjarne Stroustrup网站上找到了这个关于“朋友”关键字的有价值的答案。

“好友”是一种授予访问权限的显式机制,就像会员资格一样。

您可以坚持最严格和最纯粹的OOP原则,并确保任何类的数据成员都没有访问器,这样所有对象都必须是唯一可以知道它们的数据的对象,并且对它们进行操作的唯一方法是通过间接消息(即方法)。

但即使是c#也有一个内部可见性关键字,Java也有默认的包级可访问性。c++实际上更接近于OOP的理想,它通过精确地指定哪些其他类或只有其他类可以看到一个类,从而最大限度地减少了类的可见性。

我不太使用c++,但如果c#有朋友,我会用它来代替我经常使用的汇编全局内部修饰符。它并没有真正打破封装,因为。net中的部署单元是一个程序集。

但是还有InternalsVisibleToAttribute(otherAssembly),它的作用类似于跨组装的友元机制。微软将此用于可视化设计器程序集。

这可能不是一个实际的用例情况,但可能有助于说明类间朋友关系的使用。

会所

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++的创建者说这并没有破坏任何封装原则,我将引用他的话:

“朋友”是否违反封装? 不。但事实并非如此。“好友”是一种授予访问权限的显式机制,就像会员资格一样。你不能(在符合标准的程序中)在不修改类源的情况下授予自己对类的访问权。

很明显…

我发现了一个使用好友访问的方便地方:私有函数的Unittest。