首页 健康生活文章正文

C++类与对象的本质探析

健康生活 2025年09月05日 10:18 1 admin

各位C++战士们,欢迎来到面向对象的“觉醒时刻”!

Part1面向过程和面向对象

1.1、面向过程和面向对象的初步认识

编程思想的核心差异,从 C 语言和 C++ 的对比中可见一斑:

  • C 语言是面向过程的:关注 “步骤”,分析解决问题的流程,通过函数调用逐步执行步骤(例如,实现一个栈时,会分步骤写初始化、入栈、出栈等函数,调用时按顺序执行)。
  • C++ 是基于面向对象的:关注 “对象”,将问题拆分成不同的对象,通过对象间的交互完成任务(例如,实现栈时,将栈的属性(栈顶、容量、数据)和行为(初始化、入栈、出栈)封装成一个 “栈对象”,直接通过对象调用行为)。

面向对象编程(OOP)的核心思想是 “万物皆为对象”,将现实世界的事物抽象为程序中的对象,每个对象包含两部分:

  • 数据状态(属性,如栈的容量、当前元素个数);
  • 可执行行为(方法,如栈的入栈、出栈操作)。

面向对象的三大特性是其强大的核心:

  • 封装:将数据和行为捆绑,隐藏内部细节,只暴露必要接口(如栈的内部扩容逻辑对外隐藏,用户只需调用Push);
  • 继承:允许一个对象继承另一个对象的特性,快速构建相似对象(如 “学生” 继承 “人” 的基本属性,再添加 “学号” 等特有属性);
  • 多态:同一行为在不同对象上有不同表现(如 “动物叫”,猫会 “喵喵”,狗会 “汪汪”)。

简言之,面向对象通过 “对象” 作为基本单元,实现数据和行为的有机结合,让程序结构更清晰、易维护、可扩展。

1.2、类的引入

C 语言的结构体只能定义变量,而 C++ 对结构体进行了扩展:结构体中不仅可以定义变量,还可以定义函数。例如,用 C++ 实现栈:

typedef int DataType;struct Stack {    // 成员变量(属性)    int top;    int capacity;    DataType* array;    // 成员函数(行为)    void Init() {        top = 0;        array = (DataType*)malloc(sizeof(DataType) * 4);        if (array == nullptr) {            perror("malloc failed");            return;        }        capacity = 4;    }    void Push(DataType x) {        if (top == capacity) { // 扩容            DataType* tmp = (DataType*)realloc(array, sizeof(DataType) * (capacity * 2));            if (tmp == nullptr) {                perror("realloc failed");                return;            }            array = tmp;            capacity *= 2;        }        array[top++] = x;    }    DataType Top() {        if (top == 0) {            cout << "stack is empty" << endl;            return -1;        }        return array[top - 1];    }};int main() {    Stack ss; // C++中可省略struct关键字    ss.Init();  // 直接通过对象调用成员函数,无需传参    ss.Push(1);    ss.Push(2);    cout << ss.Top() << endl; // 输出2    return 0;}

上述代码中,Stack结构体同时包含了变量(top、capacity等)和函数(Init、Push等),且调用函数时无需像 C 语言那样传递结构体指针 —— 这是因为函数内部可以直接访问结构体的成员变量。

不过,C++ 中更推荐用class关键字定义类(而非struct),两者功能类似,但存在访问权限的默认差异(后续会讲)。

C++类与对象的本质探析

专注Linux、Unix、C/C++后端开发、面试题等技术知识讲解


1.3、类的定义

类的基本语法如下:

class ClassName {    // 类体:成员变量(属性)和成员函数(方法)}; // 注意末尾的分号
  • class是定义类的关键字;
  • ClassName是类名;
  • 类体中的变量称为成员变量(或属性);
  • 类体中的函数称为成员函数(或方法)。

类的两种定义方式

1.声明和定义全部放在类体中

成员函数在类内定义时,编译器可能将其视为内联函数(inline),适合简单函数。

class TestClass {public:    void Print() { // 类内定义,可能被视为内联        cout << _a << " " << _b << endl;    }private:    int _a; // 成员变量命名建议:加下划线区分    int _b;};

2.声明放在.h文件,定义放在.cpp文件

类声明和定义分离,适合复杂函数,避免重复编译。类外定义成员函数时,需用类名::指定作用域。

// test.h(声明)class TestClass {public:    void Print(); // 声明private:    int _a;    int _b;};// test.cpp(定义)#include "test.h"void TestClass::Print() { // 类外定义,需加TestClass::    cout << _a << " " << _b << endl;}

建议成员变量名前 / 后加下划线(如_name、age_),避免与函数参数或局部变量重名:

class Person {public:    void SetName(string name) {        _name = name; // _name是成员变量,name是参数,清晰区分    }private:    string _name; // 成员变量加下划线};

1.4、类的访问限定符及封装

1.4.1、访问限定符

C++ 通过访问限定符控制成员的访问权限,实现 “封装” 的核心功能。常用的访问限定符有三种:

C++类与对象的本质探析

  • public(公有):类外可直接访问(如对象.公有成员);
  • private(私有):类外不可直接访问,仅类内成员函数可访问;
  • protected(保护):类外不可直接访问,主要用于继承(后续讲解)。

访问限定符的作用域

从限定符出现的位置开始,到下一个限定符出现为止;若没有下一个限定符,则到类体结束(})为止。

class 与 struct 的区别

  • class定义的类,默认访问权限为 private
  • struct定义的类,默认访问权限为 public(为兼容 C 语言的结构体)。

示例:

class A {    int _a; // 默认private,类外不可访问public:    int _b; // public,类外可访问private:    int _c; // private,类外不可访问};struct B {    int _x; // 默认public,类外可访问private:    int _y; // private,类外不可访问};int main() {    A a;    a._b = 10; // 正确(public)    // a._a = 20; // 错误(private,类外不可访问)    B b;    b._x = 30; // 正确(public)    // b._y = 40; // 错误(private,类外不可访问)    return 0;}

1.4.2、封装

封装的本质:将数据(成员变量)和操作数据的方法(成员函数)有机结合,隐藏内部实现细节,仅通过公开接口与外部交互。

例如,手机的封装:用户无需知道内部芯片、电路的工作原理,只需通过屏幕、按键(接口)操作即可。

C++ 中,封装通过访问限定符实现:

  • 隐藏细节:将内部数据(如栈的array指针)设为 private,避免外部直接修改导致错误;
  • 暴露接口:将必要操作(如Push、Pop)设为 public,供外部安全调用。

1.5、类的作用域

类定义了一个独立的作用域(类域),所有成员都属于这个作用域。在类外定义成员时,需用::(作用域操作符)指明所属类域。

示例:

class Person {public:    void PrintInfo(); // 声明:属于Person类域private:    char _name[20];    int _age;};// 类外定义:需用Person::指定作用域void Person::PrintInfo() {    cout << _name << " " << _age << endl; // 可直接访问类内成员}

1.6、类的实例化

类的实例化:用类类型创建对象的过程。

  • 类是 “模型”:描述对象的属性和行为,不占用实际内存(如 “学生表” 是模型,记录学生的属性,但本身不是具体学生);
  • 对象是 “实例”:类实例化出的具体实体,占用内存(如 “张三” 是 “学生表” 实例化出的具体学生,有具体的姓名、年龄)。

示例:

class Person {public:    char _name[20];    int _age;};int main() {    Person p1; // 实例化对象p1    Person p2; // 实例化对象p2    p1._age = 18; // 对象p1的年龄    p2._age = 20; // 对象p2的年龄(不同对象的成员变量独立)    return 0;}

上述代码中,Person类本身不占内存,p1和p2是实例化的对象,各自占用内存存储_name和_age。

1.7、类的空间大小

类的空间大小仅取决于成员变量,成员函数存储在公共代码区(所有对象共享同一份函数代码)。

C++类与对象的本质探析

示例分析

#include <iostream>using namespace std;class A {}; // 空类class B {private:    int _a; // 成员变量};class C {public:    void Print() { cout << _a << endl; } // 成员函数(不占对象空间)private:    int _a; // 成员变量};int main() {    cout << sizeof(A) << endl; // 输出1    cout << sizeof(B) << endl; // 输出4    cout << sizeof(C) << endl; // 输出4    return 0;}
  • 空类(A):编译器会分配 1 字节(占位符),用于唯一标识类的实例(否则无法区分不同对象);
  • 类 B:仅含 1 个int成员变量,大小为 4 字节;
  • 类 C:成员函数不占对象空间,仅int _a占 4 字节,故大小为 4 字节。

内存对齐规则

类的成员变量存储遵循结构体对齐规则(因为类的底层存储可视为 “带函数的结构体”):

  1. 第一个成员永远放在偏移量为 0 的位置;
  2. 从第二个成员开始,每个成员需对齐到 “对齐数” 的整数倍处(对齐数 = 成员自身大小与默认对齐数的较小值;VS 默认对齐数为 8,gcc 无默认对齐数,对齐数 = 成员自身大小);
  3. 类的总大小为 “最大对齐数” 的整数倍(最大对齐数 = 所有成员对齐数的最大值);
  4. 若嵌套类 / 结构体,嵌套的类 / 结构体需对齐到自身最大对齐数的整数倍处。

示例:

class D {private:    char _c; // 对齐数1(自身大小)    int _i;  // 对齐数4(min(4, 8))    double _d; // 对齐数8(min(8, 8))};// 内存布局:// _c:偏移0(1字节)// 填充3字节(偏移1-3),使_i对齐到4// _i:偏移4(4字节)// _d:偏移8(8字节,对齐到8)// 总大小:16(最大对齐数8,16是8的整数倍)cout << sizeof(D) << endl; // 输出16

1.8、this 指针

在类的成员函数中,隐含着一个this指针,它指向当前调用函数的对象。this指针的核心作用是区分成员变量和参数(或局部变量)。

示例:this 指针的使用

class Student {private:    int _age; // 成员变量public:    void SetAge(int age) { // 参数age与成员变量_age重名        this->_age = age; // this指向当前对象,this->_age即成员变量    }    int GetAge() {        return this->_age; // 可省略this(编译器自动补充)    }};int main() {    Student s;    s.SetAge(18); // 调用时,this自动指向s    cout << s.GetAge() << endl; // 输出18    return 0;}

上述代码中,SetAge函数的参数age与成员变量_age重名,通过this->_age明确访问成员变量。

this 指针的特性

  • 类型:类类型* const(例如Student* const),即指针本身不可修改(不能给this赋值);
  • 作用域:仅在成员函数内部可用;
  • 本质:成员函数的隐含形参,当对象调用函数时,编译器自动将对象地址作为实参传递给this(无需用户手动传递);
  • 存储:不存储在对象中,通常由编译器通过寄存器(如 ecx)传递。


Part2对象的构造和析构函数

在 C++ 中,对象的创建和销毁需要特定的函数来管理初始化和清理工作,这就是构造函数和析构函数。它们是面向对象编程中确保对象正确初始化和资源释放的核心机制。

2.1、构造函数:对象的 “出生证明”

构造函数是一种特殊的成员函数,用于在创建对象时自动完成初始化工作(如分配内存、初始化成员变量等)。

核心特性:

  • 函数名与类名相同:例如class Array的构造函数名为Array。
  • 无返回值:无需声明返回类型,也不能有return语句。
  • 自动调用:在对象创建时由编译器自动调用,用户无需(也不能)手动调用。

示例:基本构造函数

#include <iostream>#include <cstdlib>using namespace std;class Array {private:    int* m_data;  // 数组数据地址    int m_size;   // 数组大小public:    // 无参构造函数(函数名与类名相同,无返回值)    Array() {        cout << "Array的无参构造函数" << endl;        m_size = 5;  // 初始化大小为5        m_data = (int*)malloc(sizeof(int) * m_size);  // 分配内存    }    // 其他成员函数(省略)    ~Array();  // 析构函数(后续讲解)};// 析构函数定义(释放资源)Array::~Array() {    cout << "Array的析构函数" << endl;    if (m_data != NULL) {        free(m_data);  // 释放动态分配的内存        m_data = NULL;    }}int main() {    Array a1;  // 创建对象时,自动调用无参构造函数    return 0;}

运行结果

Array的无参构造函数Array的析构函数  // 程序结束时自动调用析构函数

2.2、构造函数的重载与调用

和普通函数一样,构造函数支持重载(多个构造函数参数列表不同)。创建对象时,编译器会根据传递的实参匹配对应的构造函数。

调用方式:

  • 括号法:Array a(10);(最常用)。
  • 等号法:Array a = 10;(仅适用于单参数构造函数)。
  • 逗号表达式:Array a = (1, 2);(实际传递最后一个参数,此处等价于Array a(2))。

示例:重载构造函数

class Array {private:    int* m_data;    int m_size;public:    Array();                  // 无参构造函数    Array(int s);             // 单参数构造函数(指定大小)    Array(int s, int init);   // 双参数构造函数(指定大小和初始值)    ~Array();};// 无参构造函数Array::Array() {    cout << "无参构造函数" << endl;    m_size = 5;    m_data = (int*)malloc(sizeof(int) * m_size);}// 单参数构造函数Array::Array(int s) {    cout << "单参数构造函数" << endl;    m_size = s;    m_data = (int*)malloc(sizeof(int) * m_size);}// 双参数构造函数Array::Array(int s, int init) {    cout << "双参数构造函数" << endl;    m_size = s;    m_data = (int*)malloc(sizeof(int) * m_size);    for (int i = 0; i < m_size; i++) {        m_data[i] = init;  // 初始化数组元素    }}Array::~Array() {    free(m_data);    m_data = NULL;}int main() {    Array a1;         // 调用无参构造函数    Array a2(10);     // 调用单参数构造函数(括号法)    Array a3 = 8;     // 调用单参数构造函数(等号法)    Array a4(5, 0);   // 调用双参数构造函数    Array a5 = (3, 6); // 逗号表达式,实际传递6,调用单参数构造函数    return 0;}

2.3、拷贝构造函数:对象的 “复制机”

拷贝构造函数是一种特殊的构造函数,用于用已存在的对象初始化新对象

声明格式:

类名(const 类名& 源对象);  // 参数为源对象的常量引用

调用时机:

  • 用一个对象初始化另一个对象:Array a2(a1); 或 Array a2 = a1;。
  • 函数参数为对象(值传递):当函数形参是对象时,会用实参拷贝初始化形参。
  • 函数返回值为对象(值返回):返回时会拷贝一个临时对象作为返回值。

示例:拷贝构造函数

class Array {private:    int* m_data;    int m_size;public:    Array();                  // 无参构造函数    Array(const Array& a);    // 拷贝构造函数    ~Array();};Array::Array() {    cout << "无参构造函数" << endl;    m_size = 5;    m_data = (int*)malloc(sizeof(int) * m_size);}// 拷贝构造函数(此处为简化示例,实际需实现深拷贝)Array::Array(const Array& a) {    cout << "拷贝构造函数" << endl;    m_size = a.m_size;  // 复制大小    m_data = (int*)malloc(sizeof(int) * m_size);  // 分配新内存    // 后续需复制数据(深拷贝)}Array::~Array() {    free(m_data);    m_data = NULL;}// 场景2:函数参数为对象(值传递)void print(Array a) {  // 调用拷贝构造函数:用实参初始化a    // ...}// 场景3:函数返回值为对象Array createArray() {    Array temp;    return temp;  // 调用拷贝构造函数:拷贝temp到返回值}int main() {    Array a1;    Array a2(a1);  // 场景1:用a1初始化a2    print(a1);     // 场景2:触发拷贝构造函数    Array a3 = createArray();  // 场景3:触发拷贝构造函数    return 0;}

2.4、深度拷贝:避免 “同归于尽” 的复制

默认情况下,拷贝构造函数会执行浅拷贝(仅复制指针地址,不复制指针指向的内容),这会导致两个对象共享同一块内存,释放时出现 double free 错误。深拷贝则会复制指针指向的内容,确保每个对象拥有独立的资源。

示例:深拷贝的实现

Array::Array(const Array& a) {  // 深拷贝    cout << "深拷贝构造函数" << endl;    m_size = a.m_size;    // 1. 为新对象分配独立内存    m_data = (int*)malloc(sizeof(int) * m_size);    // 2. 复制源对象的数据到新内存    for (int i = 0; i < m_size; i++) {        m_data[i] = a.m_data[i];    }}

深拷贝 vs 浅拷贝

  • 浅拷贝:m_data = a.m_data;(共享内存,危险)。
  • 深拷贝:重新分配内存并复制数据(独立内存,安全)。

2.5、默认构造函数:编译器的 “默认礼物”

当用户未定义任何构造函数时,编译器会自动生成默认无参构造函数(什么也不做)。但一旦用户定义了构造函数,编译器将不再提供默认版本。

规则:

  • 若用户定义了无参构造函数,编译器不再生成默认无参构造函数。
  • 若用户定义了有参构造函数,编译器不再生成默认无参构造函数(此时创建无参对象会报错)。

示例:默认构造函数的行为

class Demo {public:    // 若注释掉以下构造函数,编译器会生成默认无参构造函数    Demo(int a) {  // 用户定义了有参构造函数        cout << "有参构造函数" << endl;    }};int main() {    // Demo d;  // 报错:无合适的无参构造函数(编译器未生成)    Demo d(10);  // 正确:调用用户定义的有参构造函数    return 0;}

2.6、析构函数:对象的 “死亡证明”

析构函数用于在对象销毁时自动完成清理工作(如释放动态内存、关闭文件等)。

核心特性:

  • 函数名:~类名(波浪线 + 类名)。
  • 无返回值:无需声明返回类型。
  • 无参数:不能重载,一个类只能有一个析构函数。
  • 自动调用:对象生命周期结束时(如离开作用域)由编译器自动调用。

示例:析构函数的作用

class Array {private:    int* m_data;    int m_size;public:    Array(int size) {        m_data = (int*)malloc(sizeof(int) * size);  // 分配内存    }    ~Array() {  // 析构函数:释放资源        cout << "释放内存" << endl;        if (m_data != NULL) {            free(m_data);  // 释放动态分配的内存            m_data = NULL;        }    }};int main() {    {        Array a(10);  // 创建对象,分配内存    }  // a离开作用域,自动调用析构函数释放内存    return 0;}

2.7、匿名对象:“来也匆匆,去也匆匆”

匿名对象是指未命名的临时对象,格式为类名(参数)。其生命周期仅在当前行,执行完后立即销毁(调用析构函数)。

示例:匿名对象的特性

class Test {public:    Test() {        cout << "构造函数" << endl;    }    ~Test() {        cout << "析构函数" << endl;    }};int main() {    Test();  // 匿名对象:构造后立即销毁    cout << "----------------" << endl;    return 0;}

运行结果

构造函数析构函数----------------

2.8、对象的动态创建和释放:new 与 delete

  • new:动态创建对象,会自动调用构造函数。
  • delete:释放动态创建的对象,会自动调用析构函数。

注意:malloc/free仅分配 / 释放内存,不会调用构造函数 / 析构函数,因此在 C++ 中应优先使用new/delete。

示例:new 与 delete 的使用

class Test {public:    Test() {        cout << "无参构造函数" << endl;    }    Test(int a) {        cout << "有参构造函数" << endl;    }    ~Test() {        cout << "析构函数" << endl;    }};int main() {    // 动态创建对象(调用有参构造函数)    Test* pt = new Test(10);    // ... 使用对象    delete pt;  // 释放对象(调用析构函数)    // 动态创建数组(调用无参构造函数)    Test* arr = new Test[3];    delete[] arr;  // 释放数组(注意加[])    return 0;}

2.9、构造函数的形参初始化列表:初始化的 “快车道”

初始化列表用于在构造函数体执行前初始化成员变量,适用于以下场景:

  1. 成员变量为const修饰(必须初始化,不能赋值)。
  2. 成员变量是类对象,且该类没有无参构造函数(必须显式初始化)。

格式:

类名(参数) : 成员1(值1), 成员2(值2), ... {    // 构造函数体}

示例:初始化列表的使用

class Date {private:    int m_year, m_month, m_day;public:    // Date没有无参构造函数,必须通过有参构造函数初始化    Date(int y, int m, int d) : m_year(y), m_month(m), m_day(d) {        cout << "Date构造函数" << endl;    }};class Student {private:    const int m_id;  // const成员必须初始化    Date birth;      // Date对象,无无参构造函数public:    // 初始化列表:初始化m_id和birth    Student(int id, int y, int m, int d) : m_id(id), birth(y, m, d) {        cout << "Student构造函数" << endl;    }};int main() {    Student s(1001, 2000, 1, 1);  // 正确初始化    return 0;}

运行结果:

Date构造函数Student构造函数


Part3静态成员变量和静态成员函数

在 C++ 中,静态成员(包括静态成员变量和静态成员函数)属于类本身,而非类的某个具体对象。它们是实现多个对象共享数据或功能的重要机制。

3.1、静态成员变量:类的 “共享仓库”

静态成员变量被所有类对象共享,不属于某个特定对象,其内存空间在程序启动时分配,生命周期贯穿整个程序。

核心特性:

  • 属于类,而非对象:所有对象访问的是同一份静态成员变量。
  • 必须在类外初始化:类内仅声明,初始化需在类外(通常在.cpp 文件中)。
  • 可通过类名直接访问:无需创建对象,格式为类名::静态变量名。
  • 普通成员函数可访问:静态成员变量对类的所有成员函数可见。

示例:静态成员变量的使用

#include <iostream>using namespace std;class Student {public:    static int count;  // 静态成员变量:记录学生总数(类内声明)    int id;            // 普通成员变量:学生编号public:    Student() {        count++;       // 每创建一个对象,总数+1        id = count;    // 用总数作为编号    }    // 普通成员函数访问静态成员变量    int GetCount() {        return count;    }};// 静态成员变量必须在类外初始化(定义)int Student::count = 0;int main() {    Student s1;    Student s2;    // 访问静态成员变量的三种方式    cout << s1.count << endl;       // 通过对象访问(不推荐)    cout << s2.count << endl;       // 通过对象访问(不推荐)    cout << Student::count << endl; // 通过类名访问(推荐)    // 通过普通成员函数访问    cout << s1.GetCount() << endl;  // 输出2    return 0;}

运行结果:

2222

3.2、静态成员函数:类的 “工具函数”

静态成员函数属于类本身,用于处理与类相关的操作,不依赖具体对象。

核心特性:

  • 无 this 指针:不能访问普通成员变量(需依赖对象),只能访问静态成员变量。
  • 可通过类名直接调用:无需创建对象,格式为类名::静态函数名()。
  • 普通成员函数不可通过类名调用:普通函数依赖对象(有 this 指针)。

示例:静态成员函数的使用

class Student {public:    static int count;  // 静态成员变量    int id;            // 普通成员变量public:    Student() {        count++;        id = count;    }    // 静态成员函数:只能访问静态成员变量    static int GetCount1() {        // return id;  // 错误:静态函数不能访问普通成员变量        return count;  // 正确:访问静态成员变量    }    // 普通成员函数:可访问静态和普通成员变量    int GetId() {        return id;    }};int Student::count = 0;int main() {    Student s1;    Student s2;    // 通过类名调用静态成员函数(推荐)    cout << Student::GetCount1() << endl;  // 输出2    // 通过对象调用静态成员函数(不推荐)    cout << s1.GetCount1() << endl;        // 输出2    // 普通成员函数必须通过对象调用    cout << s1.GetId() << endl;            // 输出1    return 0;}

静态成员的典型用途:

  • 统计类的对象总数(如示例中的count)。
  • 实现单例模式(全局唯一实例)。
  • 定义工具函数(如数学计算、数据转换等与类相关的通用操作)。


Part4友元函数与友元类

友元(friend)机制允许类外部的函数或其他类访问其私有成员,打破了类的封装性,用于解决特定场景下的访问需求(需谨慎使用,避免过度破坏封装)。

4.1、友元函数:类的 “特殊访客”

友元函数是在类中声明的外部函数(非成员函数),可以访问类的所有成员(包括私有成员)。

声明方式:

在类内用friend关键字声明函数:

class 类名 {    friend 返回类型 函数名(参数);  // 声明友元函数};

特性:

  • 友元函数不属于类,没有 this 指针。
  • 必须通过参数传递对象,才能访问该对象的成员。
  • 可访问类的 public、protected、private 成员。

示例:友元函数的使用

#include <iostream>using namespace std;class Test {    // 声明show为友元函数,允许其访问Test的所有成员    friend void show(Test& t);private:    int m_a;  // 私有成员public:    void set(int a) {        m_a = a;  // 类内函数可访问私有成员    }};// 友元函数定义(类外)void show(Test& t) {    // 友元函数可直接访问Test的私有成员m_a    cout << "Test的私有成员m_a = " << t.m_a << endl;}int main() {    Test t1;    t1.set(100);    show(t1);  // 调用友元函数,输出:Test的私有成员m_a = 100    return 0;}

4.2、友元类:类的 “信任伙伴”

若类 B 被声明为类 A 的友元类,则类 B 的所有成员函数都能访问类 A 的所有成员(包括私有成员)。

声明方式:

在类 A 中用friend class声明类 B:

class A {    friend class B;  // 声明B为A的友元类};

特性:

  • 单向性:B 是 A 的友元,但 A 不是 B 的友元(除非 B 也声明 A 为友元)。
  • 不传递性:若 B 是 A 的友元,C 是 B 的友元,C 不一定是 A 的友元。
  • 类 B 的所有成员函数都自动成为类 A 的友元函数。

示例:友元类的使用

#include <iostream>using namespace std;class A {    friend class B;  // 声明B为A的友元类private:    int m_a;  // A的私有成员public:    void set(int a) {        m_a = a;    }};class B {private:    int m_b;public:    // B的成员函数可访问A的私有成员    void print(A& a) {        cout << "A的私有成员m_a = " << a.m_a << endl;    }};int main() {    A a1;    a1.set(200);    B b1;    b1.print(a1);  // 输出:A的私有成员m_a = 200    return 0;}

友元的注意事项:

  • 友元机制破坏了类的封装性,应尽量少用。
  • 友元关系无法继承(子类不会继承父类的友元关系)。
  • 友元关系是固定的,不能在运行时动态改变。

发表评论

泰日号Copyright Your WebSite.Some Rights Reserved. 网站地图 备案号:川ICP备66666666号 Z-BlogPHP强力驱动