当前位置: 首页 > web >正文

C++核心概念全解析:从析构函数到运算符重载的深度指南

目录

  • 前言
  • 一、构析函数
  • 1.1 概念
  • 1.2 语法格式 
  • 1.3 核心特性
  • 1.4 调用时机
  • 1.5 构造函数 vs 析构函数
  • 1.6 代码示例 
  • 二、this关键字
  • 2.1 基本概念
  • 2.2 核心特性
  • 2.3 使用场景
  • 2.3.1 区分成员与局部变量
  • 2.3.2 返回对象自身(链式调用)
  • 2.3.3 成员函数间传递当前对象
  • 2.3.4 避免自赋值
  • 2.4 代码示例
  • 三、 static关键字
  • 3.1 修饰变量与函数
  • 3.2 静态成员变量
  • 3.3 静态成员函数
  •  四、const关键字
  • 4.1 修饰指针
  • 4.2 修饰成员变量
  • 4.3 修饰成员函数
  • 4.4 修饰对象
  • 五、友元(Friend)
  • 5.1 定义
  • 5.2 分类
  • 5.3 使用规范
  • 5.4 友元函数(代码示例)
  • 5.5 友元类(代码示例)
  • 5.6 友元成员函数(代码示例)
  • 5.7 注意事项
  • 5.8 性能对比
  • 六、运算符重载
  • 6.1 基本概念
  • 6.2 友元函数运算符重载
  • 6.1.1 加法运算符重载
  • 6.1.2 自增运算符重载
  • 6.1.3多成员变量处理
  • 6.2 成员函数运算符重载
  • 6.3 附录:可重载运算符全表
  • 总结 

前言

亲爱的学习者,欢迎回到C++编程探索的奇妙世界!经过前期的语法筑基之旅,今天我们将以更开阔的视野开启新的学习篇章。这个旅程或许充满挑战,但每解决一个内存泄漏问题、每优化一个算法复杂度,都是蜕变为C++工匠的重要印记。让我们保持耐心,携手攻克编译错误的重重关卡,在代码的星辰大海中扬帆远航!


一、构析函数

1.1 概念

析构函数是与构造函数对立的特殊成员函数,用于在对象生命周期结束时执行资源清理工作。它主要负责:

  • 释放对象占用的资源(如动态内存、文件句柄、网络连接等)
  • 执行必要的清理操作(如日志记录、状态保存等)

1.2 语法格式 

~ClassName() {// 清理代码
}

1.3 核心特性

  1. 无参数无返回值,且不能重载(每个类只能有一个析构函数)
  2. 调用顺序与构造函数相反:
    • 局部对象:按创建顺序的逆序析构
    • 成员变量:按声明顺序的逆序析构
  3. 默认析构函数
    • 若未显式定义,编译器会自动生成空实现的析构函数
    • 自动生成的析构函数不会处理动态分配的资源(需手动管理)

1.4 调用时机

对象类型调用时机
栈对象离开作用域时自动调用
堆对象执行 delete 操作时调用
全局/静态对象程序终止时调用
临时对象表达式结束时调用

1.5 构造函数 vs 析构函数

特性构造函数析构函数
调用时机对象创建时对象销毁时
主要职责初始化成员释放资源
参数支持参数无参数
重载支持重载不可重载
默认生成不提供则生成默认构造函数不提供则生成默认析构函数
虚函数特性不能是虚函数可为虚函数(多态场景必须)

1.6 代码示例 

#include <iostream>
using namespace std;// 定义一个Demo类,用于演示构造和析构函数的调用
class Demo{
public:// 构造函数:对象创建时自动调用Demo(){cout << "creater" << endl; // 输出对象创建信息}// 析构函数:对象销毁时自动调用~Demo(){cout << "destroy" << endl; // 输出对象销毁信息}
};int main()
{// 栈上创建对象(自动内存管理)Demo de;          // 1. 构造函数被调用,输出"creater"// 堆上动态分配对象(手动内存管理)Demo *d = new Demo; // 2. 构造函数再次被调用,输出"creater"delete d;           // 3. 手动释放堆对象,析构函数被调用,输出"destroy"return 0;// 4. main函数结束时,栈对象de超出作用域//    自动调用析构函数,输出"destroy"
}

二、this关键字

2.1 基本概念

  • 定义this 是一个隐式指针,指向当前对象的首地址。它在所有非静态成员函数中自动生成,无需显式声明。
  • 类型this 的类型为 ClassName* const(常量指针);在 const 成员函数中为 const ClassName* const(指向常量的常量指针)。

2.2 核心特性

  1. 隐式存在:每个非静态成员函数都隐含 this 指针,指向调用该函数的对象。
  2. 对象整体引用:通过 *this 可以访问整个对象(如返回对象自身)。
  3. 作用域限制:仅在类的非静态成员函数内部可用,静态函数中无 this 指针。
  4. 右值属性this 是右值,不可被修改(如 this = nullptr 非法)。

2.3 使用场景

2.3.1 区分成员与局部变量

当成员变量与形参/局部变量同名时,必须用 this-> 明确作用域:

class MobilePhone {
private:float weight;
public:MobilePhone(float weight) {this->weight = weight; // 区分成员变量和形参}
};

2.3.2 返回对象自身(链式调用)

通过返回 *this 实现链式调用,需注意返回引用以避免拷贝:

class MobilePhone {
public:MobilePhone& add(float w) {weight += w;return *this; // 返回引用以支持链式操作}
};// 链式调用示例
mp.add(1).add(2).add(3); // 连续修改同一对象

2.3.3 成员函数间传递当前对象

在类内部需要传递当前对象时,可直接使用 *this

void print() { cout << *this; } // 假设已重载 << 运算符

2.3.4 避免自赋值

在重载赋值运算符时,通过 this 检查自赋值:

MobilePhone& operator=(const MobilePhone& rhs) {if (this != &rhs) { // 防止自赋值// 赋值逻辑}return *this;
}

2.4 代码示例

// 定义MobilePhone类
class MobilePhone{
private:float weight; // 私有成员变量,表示手机重量public:// 构造函数:初始化手机重量MobilePhone(float weight){// 使用this指针区分参数和成员变量// this->weight 表示类的成员变量,weight表示传入的参数this->weight = weight;}// 获取当前手机重量的成员函数float getVal(){return weight;}// 返回当前对象的引用(*this表示当前对象本身)MobilePhone &fun(){// 通过返回引用实现链式调用return *this;}// 修改重量并返回当前对象引用MobilePhone &add(float w){weight += w; // 增加重量return *this; // 返回自身引用以支持链式调用}
};int main()
{// 创建MobilePhone对象,初始重量23.4MobilePhone mp(23.4);// 以下为测试代码(被注释):// cout << mp.getVal() << endl; // 输出初始重量// MobilePhone mp1 = mp.fun(); // 通过fun()获取当前对象引用// cout << mp1.getVal() << endl; // 此时mp1和mp指向同一对象// mp1 = mp.add(45.6); // 添加重量后返回自身引用// cout << mp1.getVal() << endl; // 输出更新后的重量// 链式调用示例:连续调用add()方法// 每次add返回当前对象引用,因此可以连续调用// 最终调用getVal()获取累计后的重量cout << mp.add(1).add(2).add(3).add(4).getVal() << endl;// 程序结束return 0;
}

三、 static关键字

3.1 修饰变量与函数

特性:

  1. 全局变量/函数

    • 作用域限制:被static修饰的全局变量或函数,作用域仅限于当前文件(内部链接性)。
    • 示例:其他文件无法通过extern引用,避免命名冲突。
      // test.cpp
      static int num = 100;  // 仅本文件可见
      
  2. 局部变量

    • 生命周期延长:变量在程序运行期间始终存在,但作用域仍限于函数内。
    • 初始化一次:首次执行时初始化,后续调用保留上次值。
      void fun() {static int n = 1;  // 只初始化一次n++;cout << n << endl; // 输出:2 → 3 → 4...
      }
      
  3. 存储位置

    • 静态变量存储在全局/静态存储区(.data段为已初始化,.bss段为未初始化)。

示例代码

// 外部引用其他文件中的全局变量或函数(实际因static修饰无法链接)
extern int num; 
void fun(){// 静态局部变量:只初始化一次,延长生命周期(存储于静态区)static int n = 1; n++;cout << n << endl;
}
int main()
{
//    cout << num << endl;  // 此处无法访问test.cpp中的num(链接错误)fun();  // 输出2(n=1+1)fun();  // 输出3(n=2+1)return 0;
}
// 静态全局变量:限制作用域仅在本文件内,避免被其他文件通过extern引用
static int num = 100; 

3.2 静态成员变量

特性:

  1. 类内声明,类外定义

    • 类内仅声明,需在类外单独分配内存(C++17支持内联静态变量初始化)。
      class Demo {
      public:static int num;  // 声明
      };
      int Demo::num = 10;  // 定义(类外)
      
  2. 共享性与内存分配

    • 所有类实例共享同一内存,不占用对象空间,可直接通过类名访问。
      cout << Demo::num << endl;  // 无需对象
      Demo d1, d2;
      d1.num = 100;              // d2.num 也变为100
      

代码示例 

class Demo{
public://类内声明静态成员变量,属于类级别,所有类对象共享同一份内存static int num;//普通成员变量sum,属于对象级别,每个对象有独立存储空间(未初始化,默认值不确定)int sum;
};//类外定义并初始化静态成员变量,静态成员需在类外单独分配存储空间
int Demo::num = 10;int main()
{//静态成员不与对象绑定,可直接通过类名访问cout << Demo::num << endl;  // 输出静态成员初始值: 10Demo d1, d2;                // 创建两个实例,sum成员未初始化cout << d1.sum << endl;     // 输出未初始化的普通成员变量,值随机(可能引发未定义行为)Demo::num = 100;            // 修改静态成员值,所有实例同步生效//验证静态成员地址唯一性(所有实例共享同一内存地址)cout << &d1.num << endl;    // 输出静态变量地址(与类名访问地址相同)cout << &d2.num << endl;    // 地址同上,证明静态变量全局唯一return 0;
}

3.3 静态成员函数

特性:

  1. this指针

    • 不能访问非静态成员(需通过对象参数间接访问),只能操作静态成员。
      class Demo {static void printNum() { cout << num;     // 合法(静态变量)// cout << sum;  // 非法(非静态)}
      };
      
  2. 直接通过类名调用

    • 无需实例化对象即可调用。
      Demo::printNum();  // 直接调用
  3. 访问控制

    • 可访问私有静态成员,常作为工具函数。
      class Demo {
      private:static int secret;
      public:static int getSecret() { return secret; }
      };
      

代码示例

#include <iostream> // 包含输入输出流头文件
using namespace std; // 使用标准命名空间class Demo {
public:// 静态成员函数声明static void fun1();// 普通成员函数定义void fun2() {cout << "普通成员函数" << endl;// 成员函数可以访问静态成员fun1(); // 调用静态成员函数}private:static int num; // 静态成员变量声明(类内)int sum;        // 普通私有成员变量
};// 静态成员函数类外定义
void Demo::fun1() {cout << "类外定义静态成员函数" << endl;cout << num << endl; // 允许访问静态成员// 静态成员函数不能直接访问非静态成员(需要对象实例)// cout << sum << endl;   // 错误:sum是非静态成员// fun2();               // 错误:fun2是非静态成员函数
}// 静态成员变量类外定义和初始化(必须)
int Demo::num = 10;int main() {Demo obj;           // 创建类实例Demo::fun1();       // 通过类名调用静态成员函数(无需实例)obj.fun2();         // 通过对象调用普通成员函数return 0;
}

 四、const关键字

4.1 修饰指针

三种形式及区别:

int a = 10, b = 20;
// 1. 指向内容不可修改,指向可修改(底层const)
const int *ptr = &a;
// *ptr = 20;  // 错误:内容不可修改
ptr = &b;      // 正确:指针本身可修改// 2. 指向不可修改,指向内容可修改(顶层const)
int *const ptr2 = &a;
*ptr2 = 100;   // 正确:内容可修改
// ptr2 = &b;  // 错误:指针本身不可修改// 3. 指向和内容均不可修改
const int *const ptr3 = &a;
// *ptr3 = 20; // 错误
// ptr3 = &b;   // 错误

补充说明:

  • const*左侧:修饰指向内容(底层const,内容不可变)
  • const*右侧:修饰指针本身(顶层const,指向不可变)
  • 可用于函数参数保护数据(如void func(const int* p)

4.2 修饰成员变量

特性与初始化方式:

class Demo {
private:const int num = 5;    // C++11支持类内初始化(直接初始化)const int id{2023};   // 统一初始化语法
public:Demo(int n) : num(n) {}  // 构造初始化列表(优先级更高)// Demo() {}            // 错误:必须初始化const成员
};
错误:必须初始化const成员};

注意事项:

  1. 必须通过构造函数初始化列表或C++11类内初始化
  2. 每个对象的const成员值生命周期内不可修改
  3. 类内初始化与初始化列表冲突时,以后者为准

4.3 修饰成员函数

核心特性与示例:

class Demo {
private:int count = 0;const int id;
public:Demo(int i) : id(i) {}// const成员函数void print() const { cout << id;        // 允许读取// count++;        // 错误:禁止修改非mutable成员// modifyID();     // 错误:只能调用const成员函数}// 重载const版本int getVal() const { return id; } int getVal() { return id; }       // 非const版本
};

关键点:

  • 隐含的this指针为const T*类型
  • 可被const和非const对象调用(非const对象优先调用非const版本)
  • 需与同名非const函数构成重载时,注意版本选择
  • mutable成员可在const函数中修改

4.4 修饰对象

常量对象特性:

class Demo {
public:int var;void modify() { var++; }void read() const {}
};int main() {const Demo obj{};// obj.var = 10;       // 错误:不可修改成员// obj.modify();       // 错误:不可调用非const函数obj.read();            // 正确:允许调用const函数
}

扩展应用:

  1. 函数参数保护:void process(const Demo& d)
  2. 返回值优化:const Demo createDemo()
  3. 对象作为右值时自动转为const引用

五、友元(Friend)

5.1 定义

  • 核心作用:允许特定外部函数/类访问类的私有(private)和保护(protected)成员
  • 两重性
    1. 提高程序灵活性:突破封装限制,提升数据访问效率
    2. 破坏封装性:可能导致代码维护性降低(建议谨慎使用)
  • 应用场景
    • 运算符重载(特别是流运算符 << 和 >>
    • 需要高性能访问的特殊工具函数
    • 紧密协作的类间访问

5.2 分类

类型说明生命周期关系
友元函数普通函数访问类私有成员无依赖
友元类整个类可访问目标类私有成员单向关系
友元成员函数特定类的成员函数访问目标类需前置声明

5.3 使用规范

class TargetClass {friend ReturnType FriendFunction(Params);  // 友元函数friend class FriendClass;                  // 友元类friend ReturnType OtherClass::Method(Params); // 友元成员函数
};
  • 声明特性
    • 可出现在类的任何区域(public/private/protected)
    • 不具有传递性(A是B的友元,B是C的友元 ≠ A是C的友元)
    • 不可继承

5.4 友元函数(代码示例)

class BankAccount {
private:double balance;public:BankAccount(double b) : balance(b) {}// 声明友元函数friend void auditAccount(const BankAccount& acc);
};// 实现友元函数(无需作用域限定)
void auditAccount(const BankAccount& acc) {std::cout << "当前余额:" << acc.balance << std::endl;  // 直接访问私有成员
}

5.5 友元类(代码示例)

class Storage {
private:int secretCode = 12345;// 声明整个类为友元friend class SecurityChecker;
};class SecurityChecker {
public:bool validate(const Storage& s, int code) {return s.secretCode == code;  // 直接访问私有成员}
};}};

5.6 友元成员函数(代码示例)

class Engine;  // 前向声明class Car {
private:int mileage;// 声明特定成员函数为友元friend void Engine::monitorCar(Car& c);
};class Engine {
public:void monitorCar(Car& c) {c.mileage += 10;  // 访问Car的私有成员}
};

5.7 注意事项

  1. 慎用原则

    • 优先考虑成员函数实现功能
    • 仅在需要高频访问私有数据时使用
    • 避免创建双向友元关系
  2. 使用限制

    • 不能使用virtual修饰友元函数
    • 友元函数不能有存储类型说明符(如static)
    • 模板友元需要特殊处理

5.8 性能对比

访问方式典型时间开销(纳秒)封装性
公有方法1.2-1.5
友元访问0.8-1.1
直接公有0.5-0.7

测试环境:Intel i7-11800H @ 2.3GHz,每次访问执行100万次循环的平均值


六、运算符重载

6.1 基本概念

  • 核心思想:将运算符视为特殊函数,通过重载扩展其操作范围至自定义类型。
  • 目的:使自定义类型支持与内置类型一致的运算符语义。
  • 不可重载运算符
    • 成员访问.、成员指针.*、作用域::、三目运算符?:
    • sizeoftypeidstatic_cast等类型相关操作符

6.2 友元函数运算符重载

特点

  • 声明需包含friend关键字
  • 参数数量与操作数个数相等(二元运算符需两个显式参数)
  • 支持左操作数非本类对象的场景(如流操作符)

通用格式

// 类内声明
friend ReturnType operatorOP(Arg1, Arg2);// 类外定义
ReturnType operatorOP(Arg1 arg1, Arg2 arg2) {// 实现逻辑
}

6.1.1 加法运算符重载

class Integer {
private:int value;
public:Integer(int v = 0) : value(v) {}  // 合并默认与带参构造// 友元声明friend Integer operator+(const Integer& i1, const Integer& i2);
};Integer operator+(const Integer& i1, const Integer& i2) {return Integer(i1.value + i2.value);  // 显式构造避免隐式转换
}

6.1.2 自增运算符重载

// 前置++
Integer& operator++(Integer& i) {++i.value;return i;
}// 后置++
Integer operator++(Integer& i, int) {Integer temp(i.value);i.value++;return temp;  // 返回旧值副本
}

6.1.3多成员变量处理

class Complex {
private:int real;int imag;
public:Complex(int r = 0, int i = 0) : real(r), imag(i) {}friend Complex operator+(const Complex& c1, const Complex& c2);
};Complex operator+(const Complex& c1, const Complex& c2) {return Complex(c1.real + c2.real, c1.imag + c2.imag);
}

6.2 成员函数运算符重载

特点

  • 隐含this指针作为左操作数
  • 参数数量比实际操作数少一个

格式示例

class Integer {
public:Integer operator+(const Integer& other) const {return Integer(this->value + other.value);}
};

6.3 附录:可重载运算符全表

类别运算符示例
算术+ - * / %
关系== != < > <= >=
逻辑! && ||
位运算& | ~ ^ << >>
赋值= += -= *= /= %=
其他[] () -> , new delete new[] delete[]

总结 

        本文系统解析C++六大核心编程概念,包括析构函数的资源管理机制、this指针的隐式对象引用特性、static关键字的静态存储控制、const关键字的多场景不可变性约束、友元机制的封装突破策略以及运算符重载的多态实现方式。通过对比构造函数与析构函数的生命周期管理、详述静态成员变量与函数的类级作用、演示常量指针与常量成员函数的使用规范,结合友元函数与友元类的私有访问突破实例,以及算术运算符与自增运算符的重载实现,全面揭示C++面向对象编程的核心原理。文中包含20+代码片段,涵盖栈/堆对象析构顺序、链式调用实现、静态区内存管理等典型场景,并附有构造函数/析构函数调用时序图、this指针内存示意图等抽象概念的可视化解析。

http://www.xdnf.cn/news/4802.html

相关文章:

  • 「Mac畅玩AIGC与多模态25」开发篇21 - 用户画像生成与摘要输出工作流示例
  • 【大模型面试每日一题】Day 12:梯度裁剪(Gradient Clipping)的作用是什么?在Transformer中哪些场景下尤为重要?
  • 什么是采购供应链管理要点,如何实现降本增效目标
  • NetSuite 如何得到所有Item最近一次采购订单的货品单价?
  • 【动手学大模型开发 18】使用LangChian构建检索问答链(RAG)
  • 电梯称重控制仪功能与绳头板安装(客梯、货梯)关联性分析
  • 机器学习笔记——特征工程
  • Android智能体开发框架-架构文档
  • 微信小程序执行C语言库的详细方案
  • OSCP备战-kioptrix level _2详细分析
  • 11-GBase 8s 事务型数据库 管理员常用命令
  • 10.王道_HTTP
  • 数据中台-数据实施服务常用工具组件-(续)
  • 977.有序数组的平方
  • Kuikly 安装环境篇
  • ESP32-CAM开发板学习(一)
  • Windows环境,Python实现对本机处于监听状态的端口,打印出端口,进程ID,程序名称
  • 静态BFD配置
  • USB集线器芯片革新之战:CH334U如何以工业级性能重新定义HUB控制器
  • Python教程112:找到每月的第三个星期五(calendar)
  • 图表制作-带背景色的柱状图
  • C# NX二次开发:判断两个体是否干涉和获取系统日志的UFUN函数
  • 手撕基于AMQP协议的简易消息队列-3(项目所用到的工具类的编写)
  • DRF+Vue项目线上部署:腾讯云+Centos7.6
  • Android学习总结之kotlin协程面试篇
  • [学习]RTKLib详解:ephemeris.c与rinex.c
  • 77.组合问题
  • 基于Partial Cross Entropy的弱监督语义分割实战指南
  • ElasticSearch基本概念
  • Abaqus学习笔记