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

#王者杯·14天创作挑战营·第1期#

目录


前言

亲爱的学习者,欢迎回到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.4
    MobilePhone 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;  // 输出静态成员初始值: 10
    
    Demo 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指针内存示意图等抽象概念的可视化解析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜松云

请投喂云云喵,谢谢喵!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值