友元friend
"朋友" (全局变量、类成员函数、类)
作用
类的私有(private)和受保护(protected)成员可以允许友元进行访问
定义
友元可以是一个非成员函数、另一个类的所有成员函数、或者另一个类本身
friend 返回类型 函数名(参数表);
friend 返回类型 类名::成员函数(参数表);
friend class 类名;
友元的应用
友元 全局函数
friend 返回类型 函数名(参数表);
cpp
class Account {
//声明showAccout全局函数为此类的友元
friend void showAccount(const Account&);
private:
int m;
protected:
int n;
public:
Account(int m,int n):m(m),n(n){}
};
//全局变量
void showAccount(const Account& ac){
//此函数是Account类的友元,则可以访问ac的私有和受保护的成员
cout << ac.m << "," << ac.n << endl;
}
int main() {
Account ac(10, 20);
showAccount(ac);
}

友元 类成员函数
friend 返回类型 类名 :: 成员函数名(参数表);
相关类可能要提前声明 ------ class类名;
cpp
class Card;
class Person {//人类
private:
string name;
public:
Person(string name) :name(name) {}
//获取卡的余额
double getMoney(Card& card);
//存钱
void pushMoney(Card& card, double v);
//取钱
void popMoney(Card& card, double v);
};
class Card {//银行卡类
//将Person类中的getMoney成员函数声明为当前类的友元
friend double Person::getMoney(Card&);
friend void Person::pushMoney(Card& , double);
friend void Person::popMoney(Card& , double);
private:
int cid;//卡号
double money;//金额
public:
Card(int cid,double money):cid(cid),money(money){}
};
//获取卡的余额
double Person::getMoney(Card& card) {
return card.money;
}
// 存钱
void Person::pushMoney(Card& card, double v) {
cout << name << "正向" << card.cid << "卡中存钱: " << v << endl;
card.money += v;
cout << "存款成功,余额:" << card.money << endl;
}
// 取钱
void Person::popMoney(Card& card, double v) {
cout << name << "正从" << card.cid << "卡中取钱: " << v << endl;
card.money -= v;
cout << "取款成功,余额:" << card.money << endl;
}
int main() {
Card card(60124, 1000);
Person p("Disen");
cout << p.getMoney(card) << endl;
p.pushMoney(card, 2000);
p.popMoney(card, 5000);
return 0;
}

友元类
friend class类名;
cpp
class Card;
class Person {//人类
private:
string name;
public:
Person(string name) :name(name) {}
//获取卡的余额
double getMoney(Card& card);
//存钱
void pushMoney(Card& card, double v);
//取钱
void popMoney(Card& card, double v);
};
class Card {//银行卡类
//将Person类中的getMoney成员函数声明为当前类的友元
/*friend double Person::getMoney(Card&);
friend void Person::pushMoney(Card& , double);
friend void Person::popMoney(Card& , double);*/
//将整个Person类作为友元
//友元的类内中的成员函数都可以访问此类的私有或者受保护的私有成员
friend class Person;
private:
int cid;//卡号
double money;//金额
public:
Card(int cid,double money):cid(cid),money(money){}
};
//获取卡的余额
double Person::getMoney(Card& card) {
return card.money;
}
// 存钱
void Person::pushMoney(Card& card, double v) {
cout << name << "正向" << card.cid << "卡中存钱: " << v << endl;
card.money += v;
cout << "存款成功,余额:" << card.money << endl;
}
// 取钱
void Person::popMoney(Card& card, double v) {
cout << name << "正从" << card.cid << "卡中取钱: " << v << endl;
card.money -= v;
cout << "取款成功,余额:" << card.money << endl;
}
int main() {
Card card(60124, 1000);
Person p("Disen");
cout << p.getMoney(card) << endl;
p.pushMoney(card, 2000);
p.popMoney(card, 5000);
return 0;
}
friend语句的位置
类中任意的位置
静态类成员
静态成员变量
C++11之前
的声明与定义: 类中声明,类外定义
cpp
class A {
public:
int x;
static int y;//静态成员变量,此处只是声明
};
//对于A类中的静态变量进行定义
int A::y = 0;//存储在静态全局初始化区
int main() {
A a1;
//普通类成员变量的访问
a1.x = 100;
cout << a1.x << endl;
//类的静态成员变量的访问
a1.y = 5;
cout << "y:" << a1.y << "==" << A::y << endl;
}
C++11
通过constexpr 关键字允许在类中定义
(可以指定初始值) constexpr 表示静态变量为常数,不能修改。另外,如果取此静态变量地址时,还需要在类外定义。
cpp
class B {
public:
static constexpr int x = 0;
};
//constexpr int B::x;
int main() {
cout << &B::x << endl;
auto p = &B::x;
cout << B::x << endl;
return 0;
}
C++17
constexpr + inline 不需要在类的外部定义
cpp
class B {
public:
static inline constexpr int x = 0;
};
静态成员变量是否存储在类对象空间中?
静态成员变量不会存储在对象空间中(不占对象空间)
静态成员函数
不能访问普通的成员(属性、函数),只能访问静态成员。
cpp
class C {
public:int x;
static constexpr double PI = 3.1415926;
void setX(int x) {
this->x = x;
}
double area() {
//普通成员函数内可以访问自己的静态成员
return x * x * PI;
}
static C getC() {
// 静态成员函数:只是属于类的,但是与对象无关。所以其内 不存在this。
// 不能访问普通的成员(属性、函数)
// 只能访问静态成员
C c1;
c1.setX(10);
return c1;
}
};
int main() {
C c = C::getC(); // 类名::静态成员函数() 访问
cout << c.area() << endl;
C c2;
c2 = c2.getC(); // 类对象.静态成员函数() 访问
cout << c2.x << endl;
return 0;
}
存在this吗?
不存在this,只是属于类,但是与对象无关,所以其内不存在this指针
两者访问方式
1)类名::
- 对象. 或类对象指针->
常用对象与对象的关系
依赖
构造函数、成员函数的形参上依赖其它类的关系。
一个类以局部变量、函数参数或返回值的形式临时使用另一个类。
关系是短暂的,通常不通过成员变量持有对方。
关联
成员属性上(单向关联,双向关联)。
一个类持有另一个类的指针或引用作为成员,但双方生命周期独立,可以互相存在而不依赖对方。
关联可以是单向或双向的。
聚合
一种特殊的关联,表示"整体-部分"关系,但部分可以脱离整体独立存在。(整体和部分的关系, 部分可以单独存在)
整体拥有部分,但部分的生命周期不受整体控制。
组合
更强的"整体-部分"关系,部分的生命周期完全由整体控制。(强聚合关系, 部分不能独立存在)
整体被销毁时,部分也随之销毁。通常通过值成员或独占指针实现。
cpp
class Line;
class Point {
friend class Line;
private:
int x, y;
public:
Point(int x, int y) : x(x), y(y) {}
};
class Line {
private:
// Line 关联2上Point
Point* p1;
Point* p2;
public:
// 关联的体现(聚合)
Line(Point* p1, Point* p2) {
this->p1 = p1;
this->p2 = p2;
}
void draw() {}
// Line::draw()成员函数 依赖Point
void draw(Point& p1, Point& p2) {}
};
int main() {
Point p1(1, 2), p2(2, 4), p3(5, 6);
// 聚合关系, 整体与部分的关系
{
Line l1(&p1, &p2);
Line l2(&p1, &p3);
}
// 整体对象释放了,不影响部分
return 0;
}
类模板(重点)
泛型与具体类型的关系(模板实例化的关系)
cpp
//类模板
template<class T>
class Num {
private:
T v;
public:
Num(const T& v) :v(v) {}
T getV() { return v; }
void operator+=(Num& o) {
v += o.v;
}
};
int main() {
// 泛型具体类 与类模板的关系
Num<int> n1(20), n2(30);
n1 += n2;
cout << n1.getV() << endl;
Num<float> n3(2.5f);
cout << n3.getV() << endl;
return 0;
}

当类模板作为函数形参时,必须是模板实例(具体化)
类模板遇到友元时,必须指定模板的具体实例
cpp
template<class T>
class Array {
// 类模板遇到友元时,必须指定模板的具体实例
template<class E>
friend void showArray(Array<E> arr);
private:
T* p;
int i;
int max_size;
public:
Array(int max_size) :max_size(max_size) {
i = 0;
p = new T[max_size]{ T() };
}
int add(T item) {
if (i >= max_size) return -1;
p[i++] = item;
}
int remove(T item) {
if (i == 0) return -1;
for (int j = 0; j < i; j++) {
if (p[j] == item) {
int k = j;
while (k < i - 1) {
p[k] = p[k + 1];
k++;
}
p[k] = 0;
i--;
return j;
}
}
return -1;
}
T get(int idx) {
// 下标越界异常
if (idx >= i) throw exception("index out of range");
return p[idx];
}
int size() {
return i;
}
};
// 1. 当类模板作为函数形参时,必须是模板实例(具体化)
template<class E>
void showArray(Array<E> arr) {
for (int i = 0; i < arr.max_size; i++) {
cout << arr.get(i) << " ";
}
cout << endl;
}
int main() {
Array<string> a1(10);
a1.add("disen");
a1.add("Lucy");
a1.add("Mack");
//a1.remove(15);
cout << a1.size() << endl;
showArray(a1);
Array<int> a2(10);
a2.add(1);
a2.add(2);
showArray(a2);
return 0;
}