第十一章:使用类 | 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++ Primer Plus》为蓝本,逐章提炼必考知识点、易错点、面试高频考点,跳过冗余示例,直击语法本质与工程实践,帮你高效吃透 C++ 基础,夯实底层开发必备能力。

全部评论

相关推荐

评论
3
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务