目录
1.使用示例
1.商品类
2.促销类
2.友元的分类
3.作用域
1.类
2.全局函数
3.成员函数
友元是一种机制,允许将类的非公有成员授权给外部类或函数访问。 友元通过friend关键字声明,只能出现在类定义内部。
我们来举一个例子,假设我们有一个商品类Product,还有一个促销活动类Promotion。
商品类有3个数据成员,分别代表商品名称、所属类目、商品价格,正常情况下数据成员都是private的
#include
#include
#include
#include
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) {}
};
促销活动类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 categoryIds;
public:
double discount(Product& p);
Promotion(std::vector &vec) : categoryIds(vec) {}
};
double Promotion::discount(Product& p) {
std::vector::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 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; // 输出不打折的价格
}
上面的例子中我们将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) {}
};
之前的示例中,Product类将Promotion类声明为自己的友元类,而此时Promotion类尚未定义。之所以能运行是因为friend class Promotion除了把Promotion声明为Product的友元类,还完成了Promotion类的声明,此时Promotion是已声明的不完全类型(incomplete type)。
将全局函数声明为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;
}
友元成员函数就不能先定义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 categoryIds;
public:
double discount(Product& p);
Promotion(std::vector& vec) : categoryIds(vec) {}
};
只把Promotion的定义前移也不足以解决问题,因为Promotion::discount的定义依赖Product类型,这时候需要提前声明Product为不完全类型。完整流程如下
class Product;
class Promotion {
private:
std::vector categoryIds;
public:
double discount(Product& p);
Promotion(std::vector& 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::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;
}