12.4 友元

目录

1.使用示例

        1.商品类

        2.促销类

2.友元的分类

3.作用域

        1.类

        2.全局函数

        3.成员函数


友元是一种机制,允许将类的非公有成员授权给外部类或函数访问。 友元通过friend关键字声明,只能出现在类定义内部。

1.使用示例

我们来举一个例子,假设我们有一个商品类Product,还有一个促销活动类Promotion。

1.商品类

商品类有3个数据成员,分别代表商品名称、所属类目、商品价格,正常情况下数据成员都是private的

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

class Product {
private:
    std::string productName;
    unsigned long categoryId;
    double price;
public:
    Product(std::string pname, unsigned long cid, double prc) : productName(pname), categoryId(cid), price(prc) {}
    Product() : categoryId(0), price(0) {}
};
2.促销类

促销活动类Promotion提供一个接口discount,根据商品所属的类目,判断商品是否参与促销,并计算促销后的价格。Promotion会大量访问Product的私有字段,但是这些私有字段又不希望被其他类/函数访问。可以将Promotion定义为Product的友元类

class Product {
friend class Promotion;  // 定义Promotion为友元类
private:
    std::string productName;
    unsigned long categoryId;
    double price;
public:
    Product(std::string pname, unsigned long cid, double prc) : productName(pname), categoryId(cid), price(prc) {}
    Product() : categoryId(0), price(0) {}
};

设置为友元类后,Promotion的成员函数就可以无障碍的访问Product的私有字段

class Promotion {
private:
    std::vector<unsigned long> categoryIds;
public:
    double discount(Product& p);
    Promotion(std::vector<unsigned long> &vec) : categoryIds(vec) {}
};

double Promotion::discount(Product& p) {
    std::vector<unsigned long>::iterator it = std::find(categoryIds.begin(), categoryIds.end(), p.categoryId);
    if (it == categoryIds.end()) { // 找不到,商品所属类目为参加促销
        return p.price;
    }
    else { // 参加促销
        return p.price * 0.9;
    }
}

下面是一段这两个类的测试代码

int main() {
    Product p1("Nike篮球鞋", 11UL, 9.8);
    Product p2("遮阳帽", 22UL, 6.6);

    std::vector<unsigned long> promotionCategoryIds;
    promotionCategoryIds.push_back(11UL);

    Promotion prom(promotionCategoryIds);

    std::cout << "p1.discount: " << prom.discount(p1) << std::endl; // 输出打9折的价格
    std::cout << "p2.discount: " << prom.discount(p2) << std::endl; // 输出不打折的价格
}
2.友元的分类

上面的例子中我们将Promotion类设置为Product的友元类,除了设置整个类为友元类。C++还支持将类的成员函数、全局函数设置为友元。设置的语法格式如下

class Product {
friend class Promotion;                           // 设置友元类
friend double discount(Product& p);               // 设置友元函数
friend double Promotion::discount(Product& p);    // 设置友元成员函数
private:
    std::string productName;
    unsigned long categoryId;
    double price;
public:
    Product(std::string pname, unsigned long cid, double prc) : productName(pname), categoryId(cid), price(prc) {}
    Product() : categoryId(0), price(0) {}
};
3.作用域
1.类

之前的示例中,Product类将Promotion类声明为自己的友元类,而此时Promotion类尚未定义。之所以能运行是因为friend class Promotion除了把Promotion声明为Product的友元类,还完成了Promotion类的声明,此时Promotion是已声明的不完全类型(incomplete type)。

2.全局函数

将全局函数声明为Product的友元函数时,同样可以先定义Product类,再定义函数,因为friend double discount(Product& p)同时完成了函数的声明。

class Product {
friend double discount(Product& p);
private:
    std::string productName;
    unsigned long categoryId;
    double price;
public:
    Product(std::string pname, unsigned long cid, double prc) : productName(pname), categoryId(cid), price(prc) {}
    Product() : categoryId(0), price(0) {}
};

double discount(Product& p) {
    return p.price;
}
3.成员函数

友元成员函数就不能先定义Product了,下面这段代码是编译不通过的,因为声明成员函数为友元的时候,Promotion并不存在。

class Product {
friend double Promotion::discount(Product& p);
private:
    std::string productName;
    unsigned long categoryId;
    double price;
public:
    Product(std::string pname, unsigned long cid, double prc) : productName(pname), categoryId(cid), price(prc) {}
    Product() : categoryId(0), price(0) {}
};

class Promotion {
private:
    std::vector<unsigned long> categoryIds;
public:
    double discount(Product& p);
    Promotion(std::vector<unsigned long>& vec) : categoryIds(vec) {}
};

只把Promotion的定义前移也不足以解决问题,因为Promotion::discount的定义依赖Product类型,这时候需要提前声明Product为不完全类型。完整流程如下

  1. 声明Product
  2. 定义Promotion类,因为Promotion依赖Product类,所以第1步必须要做
  3. 定义Product,将Promotion::discount定义为友元
  4. 定义Promotion::discount,因为discount会访问Product的数据成员,这一步必须在Product定义之后做
class Product;

class Promotion {
private:
    std::vector<unsigned long> categoryIds;
public:
    double discount(Product& p);
    Promotion(std::vector<unsigned long>& vec) : categoryIds(vec) {}
};

class Product {
friend double Promotion::discount(Product& p);
private:
    std::string productName;
    unsigned long categoryId;
    double price;
public:
    Product(std::string pname, unsigned long cid, double prc) : productName(pname), categoryId(cid), price(prc) {}
    Product() : categoryId(0), price(0) {}
};


double Promotion::discount(Product& p) {
    std::vector<unsigned long>::iterator it = std::find(categoryIds.begin(), categoryIds.end(), p.categoryId);
    if (it == categoryIds.end()) { // 找不到,商品所属类目为参加促销
        return p.price;
    }
    else { // 参加促销
        return p.price * 0.9;
    }
    return 0;
}

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值