第十一章:使用类 | C++ Primer Plus 重点带看
运算符重载
class Complex {
private:
double real, imag;
public:
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
};
Complex a(1, 2), b(3, 4);
Complex c = a + b; // 等价于 Complex c = a.operator+(b);
// a 是调用对象,b 是参数
用运算符重载实现前置 ++ 和后置 ++
class Counter {
private:
int value;
public:
// 前置 ++
Counter& operator++() {
++value;
return *this;
}
// 后置 ++(int 参数区分)
Counter operator++(int) {
Counter temp = *this;
++value;
return temp;
}
};
Counter c;
++c; // c.operator++()
c++; // c.operator++(0)
运算符类型 | 参数个数 | 调用方式 |
一元(前置) | 0 | obj.op( ) |
一元(后置) | 1 (int) | obj.op(0) |
二元 | 1 | obj.op(arg) |
二元(全局) | 2 | op(left, right) |
运算符重载的限制
- 至少有一个操作数是用户自定义类型;
// ✗ 错误:不能重载基本类型的运算符
int operator+(int a, int b) { return a - b; }
// ✓ 正确:至少有一个是自定义类型
Complex operator+(int a, const Complex& c) { /* ... */ }
- 必须遵循原来的语法与优先级;
- 不能创建新的运算符;
- 禁止重载的运算符:::、?: 、. 、.*、 sizeof、typeid 及类型转换运算符;
- 只能通过成员函数重载的运算符:=、( )、[ ]、->及类型转换;
- 特殊运算符
// new/delete 可以重载(静态成员函数,即使不写 static,也是隐式静态的) void* operator new(size_t size); void operator delete(void* ptr); // 输入输出运算符必须是非成员函数(友元) friend ostream& operator<<(ostream& os, const Complex& c); friend istream& operator>>(istream& is, Complex& c);
三种友元
友元有三种:友元函数(单个函数可访问)、友元类(整个类的成员函数可访问)、友元成员函数(仅特定成员函数可访问)。友元关系是单向的、不可传递、不可继承。并且友元破坏封装性,应遵循最小权限原则谨慎使用。
注意事项:
注意事项 | 说明 |
友元不是成员 | 无 this,不能通过对象调用 |
声明位置无关 | public/private/protected 效果相同 |
单向性 | A 是 B 的友元,B 不一定是 A 的友元 |
不可传递 | A→B→C,C 不能访问 A |
不可继承 | 父类的友元不能访问子类私有成员 |
破坏封装 | 应谨慎使用,遵循最小权限原则 |
非成员重载运算符与友元
非成员运算符重载与友元体现了"服务器友好-客户警惕"原则:类(服务器)主动声明哪些外部函数(客户)为友元,使其可访问私有成员;未被声明的函数无法访问。这保持了封装性的同时提供必要的访问途径。典型应用是 << 运算符重载。
"服务器友好-客户警惕"的含义:
- 服务器友好:类(服务器)愿意对特定函数开放私有成员访问(友元声明)
- 客户警惕:外部函数(客户)必须被显式声明为友元才能访问私有成员,不是谁都能访问
友元函数与成员函数区别
特性 | 友元函数 | 成员函数 |
是否属于类 | ❌ 否 | ✅ 是 |
调用方式 | func(obj) | obj.func() |
this 指针 | ❌ 无 | ✅ 有 |
访问私有成员 | ✅ 可以 | ✅ 可以 |
声明位置 | 类内部 | 类内部 |
定义位置 | 类外部(也可以在类内定义,但是很少使用) | 类内部或外部 |
单参数构造函数与隐式转换
- 单参数构造函数 → 编译器可自动进行隐式类型转换。
- explicit 关键字 → 禁止隐式转换,必须显式调用。
#include <iostream>
class Lei {
private:
int a;
public:
// 单参数构造函数:允许隐式转换
Lei(int value) : a(value) {
std::cout << "构造: " << a << std::endl;
}
int getValue() const { return a; }
};
void printLei(Lei lei) {
std::cout << "值: " << lei.getValue() << std::endl;
}
int main() {
// 正常构造
Lei lei1(10);
// 隐式转换:int → Lei
Lei lei2 = 20; // 编译器自动调用 Lei(20)
// 函数参数隐式转换
printLei(30); // 编译器自动将 30 转换为 Lei(30)
return 0;
}
类型转换函数
转换函数语法:operator 目标类型()
- 限制 1:必须是类方法(成员函数):因为转换函数要访问类的私有成员,且转换的是"本类对象"到其他类型。
- 限制 2:不能指定返回类型(返回类型就是 typeName):返回类型已经由函数名指定了,写返回类型是多余的,编译器自动知道。
- 限制 3:不能有参数:转换是一元的:本对象 → 目标类型 ,不需要额外参数。
#include <iostream>
#include <string>
class Number {
private:
double value;
public:
Number(double v = 0) : value(v) {}
// 转换为 int
operator int() const {
std::cout << "调用 operator int()" << std::endl;
return static_cast<int>(value);
}
// 转换为 double
operator double() const {
std::cout << "调用 operator double()" << std::endl;
return value;
}
// 转换为 bool
operator bool() const {
std::cout << "调用 operator bool()" << std::endl;
return value != 0;
}
};
int main() {
Number num(3.14);
// 隐式转换
int i = num; // 调用 operator int()
double d = num; // 调用 operator double()
bool b = num; // 调用 operator bool()
// 显式转换
std::cout << static_cast<int>(num) << std::endl;
// 在表达式中自动转换
std::cout << num + 1 << std::endl; // 转换为 double 或 int
if (num) { // 转换为 bool
std::cout << "num is true" << std::endl;
}
return 0;
}
C++ Primer Plus 精读|从入门到面试,重点内容全程带看。 本专栏以《C++ Primer Plus》为蓝本,逐章提炼必考知识点、易错点、面试高频考点,跳过冗余示例,直击语法本质与工程实践,帮你高效吃透 C++ 基础,夯实底层开发必备能力。
查看6道真题和解析