一、什么是友元(friend)

友元是一种特殊的关系,允许某个函数或类访问另一个类的私有(private)和保护(protected)成员。
友元不是类的成员,但拥有类的访问权限。


二、友元的类型

  1. 友元函数
    普通函数或类外的函数,被声明为某类的 friend,可以访问该类的私有成员。

  2. 友元类
    某个类被声明为另一个类的 friend,可以访问该类的所有私有和保护成员。

  3. 友元成员函数
    某个类的成员函数被声明为另一个类的 friend,可以访问该类的私有和保护成员。


三、语法与代码示例

1. 友元函数

#include <iostream>

class Box {
private:
    int length;
public:
    Box(int l) : length(l) {}
    friend void printLength(const Box& b); // 声明友元函数
};

void printLength(const Box& b) {
    std::cout << "Length: " << b.length << std::endl; // 访问私有成员
}

int main() {
    Box box(10);
    printLength(box);
    return 0;
}

2. 友元类

#include <iostream>

class Box;

class BoxHelper {
public:
    void showLength(const Box& b);
};

class Box {
private:
    int length;
public:
    Box(int l) : length(l) {}
    friend class BoxHelper; // 声明 BoxHelper 为友元类
};

void BoxHelper::showLength(const Box& b) {
    std::cout << "Length: " << b.length << std::endl; // 访问私有成员
}

int main() {
    Box box(20);
    BoxHelper helper;
    helper.showLength(box);
    return 0;
}

3. 友元成员函数

#include <iostream>

class Box;

class BoxHelper {
public:
    void showLength(const Box& b);
};

class Box {
private:
    int length;
public:
    Box(int l) : length(l) {}
    friend void BoxHelper::showLength(const Box& b); // 只授权某成员函数为友元
};

void BoxHelper::showLength(const Box& b) {
    std::cout << "Length: " << b.length << std::endl;
}

int main() {
    Box box(30);
    BoxHelper helper;
    helper.showLength(box);
    return 0;
}

四、底层实现原理

  • 友元不是成员:友元函数/类不是该类的成员,不受类的访问控制限制。
  • 编译期处理:编译器在编译时赋予友元访问权限,生成的代码可以直接访问私有成员,无需额外运行时机制。
  • 不破坏封装:友元机制只在特定场景下开放访问权限,不影响类的整体封装性。

五、应用场景

  • 操作符重载:如 operator<<,需要访问私有成员但不是成员函数。
  • 类间紧密协作:如数据结构的节点类和管理类。
  • 调试/测试:辅助函数需要访问私有成员。
  • 实现某些设计模式:如工厂模式、代理模式等。

六、注意事项

  • 滥用友元会破坏封装性,应谨慎使用。
  • 友元关系是单向的,A是B的友元,B不一定能访问A的私有成员。
  • 友元声明可以在类的任何位置(public/private/protected),但访问权限一样。

七、补充:友元与继承

  • 友元关系不会被继承,子类不会自动成为父类的友元。

八、C++ 异常机制简介

C++ 异常用于在程序运行过程中处理错误和异常情况,实现错误检测与处理的分离。异常机制基于三个关键字:

  • try:用于包裹可能发生异常的代码块。
  • throw:用于抛出异常对象。
  • catch:用于捕获和处理异常。

九、基本语法和代码示例

1. 基本用法

#include <iostream>

int divide(int a, int b) {
    if (b == 0)
        throw "除数不能为0"; // 抛出异常(字符串字面量)
    return a / b;
}

int main() {
    try {
        std::cout << divide(10, 0) << std::endl;
    } catch (const char* msg) {
        std::cout << "异常: " << msg << std::endl;
    }
    return 0;
}

2. 抛出标准异常对象

C++ 标准库定义了一系列异常类(如 std::exception 及其派生类):

#include <iostream>
#include <stdexcept>

int divide(int a, int b) {
    if (b == 0)
        throw std::runtime_error("除数不能为0");
    return a / b;
}

int main() {
    try {
        std::cout << divide(10, 0) << std::endl;
    } catch (const std::exception& e) {
        std::cout << "异常: " << e.what() << std::endl;
    }
    return 0;
}

3. 捕获所有异常

try {
    // code
} catch (...) {
    std::cout << "捕获到未知异常!" << std::endl;
}

十、异常类层次结构

C++ 标准库异常类位于 <exception> 头文件:

  • std::exception(基类)
    • std::logic_error
      • std::invalid_argument
      • std::domain_error
      • std::length_error
      • std::out_of_range
    • std::runtime_error
      • std::overflow_error
      • std::underflow_error
      • std::range_error

你可以自定义异常类:

class MyException : public std::exception {
public:
    const char* what() const noexcept override {
        return "自定义异常";
    }
};

十一、底层实现原理(简述)

  • try-catch块:编译器为每个 try 块生成异常处理表。
  • throw:抛出异常时,编译器会搜索匹配的 catch 块,并展开栈(调用析构函数,释放资源),直到找到匹配的处理器。
  • 资源释放:保证局部对象的析构函数被调用(RAII原则)。
  • 性能:异常处理机制有一定开销,通常只用于错误处理,不用于普通流程控制。

十二、应用场景

  • 文件、网络、数据库等 I/O 操作失败
  • 内存分配失败(new 抛出 std::bad_alloc
  • 算法或业务逻辑错误(如除零、越界)
  • 标准库容器操作(如访问非法索引)
  • 需要保证资源安全释放时(RAII)

十三、异常安全(Exception Safety)

  • 基本保证:异常发生时,程序状态不变,不泄漏资源。
  • 强保证:异常发生时,操作要么成功,要么无副作用。
  • 无异常保证:承诺不会抛出异常(如 noexcept)。
void foo() noexcept; // 承诺不抛异常

十四、注意事项

  • 不建议抛出基本类型(如 int、char*),推荐抛出异常类。
  • 析构函数中不建议抛异常,否则可能导致程序终止。
  • 异常处理会影响性能,关键代码可考虑使用错误码而不是异常。
  • C++ 异常与 C 的 setjmp/longjmp 不兼容。

十五、补充:异常与 RAII

异常发生时,局部对象会自动析构,资源安全释放:

#include <iostream>

class File {
public:
    File() { std::cout << "打开文件\n"; }
    ~File() { std::cout << "关闭文件\n"; }
};

int main() {
    try {
        File f;
        throw std::runtime_error("出错了");
    } catch (...) {
        std::cout << "异常被捕获\n";
    }
    // 输出顺序:打开文件 -> 关闭文件 -> 异常被捕获
}

创作不易、点赞关注、持续创作!!!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐