C++面向对象6——析构函数 、静态成员

析构函数

C++析构函数详解

一、析构函数的基本概念

析构函数是类的特殊成员函数,用于在对象生命周期结束时执行清理工作。其特点:

  • 名称:类名前加波浪号~(如~ClassName()
  • 无参数:不能重载,每个类只有一个析构函数
  • 无返回值:隐式返回void
  • 自动调用:对象销毁时系统自动调用
  • 不可手动调用:虽可显式调用,但会导致重复析构

示例:

class Resource {
private:
    int* data;
public:
    Resource(int size) {
        data = new int[size];  // 分配内存
        cout << "Resource created" << endl;
    }
    
    ~Resource() {
        delete[] data;  // 释放内存
        cout << "Resource destroyed" << endl;
    }
};
二、析构函数的调用时机
  1. 栈对象:作用域结束时
    void func() {
        Resource res(10);  // 构造函数调用
    }  // 作用域结束,析构函数自动调用
    
  2. 堆对象delete操作时
    Resource* ptr = new Resource(10);
    delete ptr;  // 显式调用析构函数
    
  3. 全局/静态对象:程序结束时
    static Resource globalRes(10);  // 程序启动时构造
    // 程序结束时析构
    
  4. 容器元素:容器销毁或元素被移除时
    vector<Resource> vec;
    vec.push_back(Resource(10));  // 临时对象被拷贝后析构
    // vec离开作用域时,所有元素析构
    
三、析构函数的核心作用
  1. 释放动态资源

    ~Resource() {
        delete[] data;  // 释放堆内存
        close(fileHandle);  // 关闭文件句柄
        free(ptr);  // 释放C风格内存
    }
    
  2. 关闭外部资源

    ~DatabaseConnection() {
        disconnect();  // 断开数据库连接
    }
    
  3. 通知其他对象

    ~Observer() {
        subject->unregister(this);  // 从观察者列表移除
    }
    
四、析构函数的关键特性
  1. 隐式生成的析构函数

    • 若未定义析构函数,编译器会自动生成一个
    • 隐式析构函数会调用成员对象和基类的析构函数
  2. 虚析构函数(多态析构)

    • 作用:确保通过基类指针删除派生类对象时,正确调用派生类析构函数
    class Base {
    public:
        virtual ~Base() { cout << "Base destroyed" << endl; }
    };
    
    class Derived : public Base {
    public:
        ~Derived() { cout << "Derived destroyed" << endl; }
    };
    
    Base* ptr = new Derived();
    delete ptr;  // 输出:Derived destroyed → Base destroyed
    
    • 原则:基类有虚函数时,析构函数必须声明为virtual
  3. 禁止析构函数抛出异常

    • C++11后默认noexcept,若可能抛出异常需显式声明
    ~Resource() noexcept(false) {  // 允许抛出异常
        // ...
    }
    
    • 若析构函数抛出异常且未捕获,程序将终止(std::terminate
  4. 析构顺序

    • 派生类析构函数成员对象析构函数基类析构函数
    class Member {
    public:
        ~Member() { cout << "Member destroyed" << endl; }
    };
    
    class Base {
    public:
        ~Base() { cout << "Base destroyed" << endl; }
    };
    
    class Derived : public Base {
        Member m;  // 成员对象
    public:
        ~Derived() { cout << "Derived destroyed" << endl; }
    };
    
    // 析构输出:Derived → Member → Base
    
五、特殊场景处理
  1. 智能指针与析构

    class Resource {
    public:
        ~Resource() { cout << "Resource destroyed" << endl; }
    };
    
    // 自动管理生命周期
    std::unique_ptr<Resource> ptr = std::make_unique<Resource>();
    // ptr离开作用域时自动释放资源
    
  2. 异常安全的析构

    ~Resource() {
        try {
            cleanup();  // 可能抛出异常的清理操作
        } catch (...) {
            // 记录错误但不传播异常
        }
    }
    
  3. 禁止对象析构

    class NonDestroyable {
    private:
        ~NonDestroyable() {}  // 私有析构函数
    public:
        static NonDestroyable* create() {
            return new NonDestroyable();  // 只能通过new创建
        }
        void release() { delete this; }  // 提供释放接口
    };
    
六、与构造函数的对比
特性构造函数析构函数
名称与类名相同类名前加~
参数可重载,支持参数列表不可重载,无参数
返回值
调用时机对象创建时对象销毁时
默认生成若无构造函数,编译器生成若无析构函数,编译器生成
虚函数不能为虚函数常需声明为虚函数(基类)
七、常见误区与注意事项
  1. 内存泄漏风险

    • 若析构函数未释放动态分配的内存,会导致泄漏
    ~BadClass() {
        // 忘记 delete ptr;
    }
    
  2. 双重释放问题

    • 浅拷贝导致多个对象指向同一资源,析构时重复释放
    class Bad {
        int* ptr;
    public:
        Bad() { ptr = new int; }
        ~Bad() { delete ptr; }  // 若拷贝未处理,会双重释放
    };
    
  3. 在析构函数中调用虚函数

    • 派生类部分已析构,虚函数调用可能失效(调用基类版本)
  4. 静态对象析构的顺序问题

    • 不同编译单元的静态对象析构顺序未定义,可能导致依赖问题
八、析构函数总结
  • 核心价值:确保资源释放,防止内存泄漏和资源耗尽
  • 最佳实践
    • 动态资源(内存、文件、网络连接)必须在析构函数中释放
    • 基类析构函数声明为virtual
    • 避免析构函数抛出异常
    • 遵循RAII(资源获取即初始化)原则
  • 现代C++趋势
    • 优先使用智能指针替代原始指针
    • 通过移动语义减少拷贝和析构开销

合理设计析构函数是编写健壮C++代码的基础。

静态成员

C++静态成员详解

一、静态成员变量

静态成员变量属于类本身,而非某个具体对象,所有对象共享同一份数据。

特点:

  • 内存分配:存储在全局数据区,程序运行期间仅一份实例
  • 声明与定义:类内声明,类外定义并初始化(需指定类作用域)
  • 访问方式:通过类名直接访问(ClassName::staticVar)或通过对象访问

示例:

class Account {
public:
    static double interestRate;  // 类内声明静态成员变量
    int id;
    double balance;
};

// 类外定义并初始化静态成员变量
double Account::interestRate = 0.05;

int main() {
    Account::interestRate = 0.06;  // 通过类名访问
    Account a1, a2;
    a1.interestRate = 0.07;  // 通过对象访问(不推荐)
    cout << Account::interestRate << endl;  // 输出0.07
}
二、静态成员函数

静态成员函数不依赖于任何对象,只能访问静态成员(变量和函数)。

特点:

  • 无this指针:无法访问非静态成员
  • 调用方式:通过类名直接调用(ClassName::staticFunc()
  • 权限控制:可设为private/protected,限制访问

示例:

class Counter {
private:
    static int count;  // 静态成员变量:记录实例个数
public:
    Counter() { count++; }
    ~Counter() { count--; }
    
    static int getCount() {  // 静态成员函数
        return count;
    }
};

int Counter::count = 0;  // 初始化静态成员变量

int main() {
    cout << Counter::getCount() << endl;  // 输出0
    
    Counter c1, c2;
    cout << Counter::getCount() << endl;  // 输出2
    
    {
        Counter c3;
        cout << Counter::getCount() << endl;  // 输出3(局部作用域)
    }  // c3析构,count减1
    
    cout << Counter::getCount() << endl;  // 输出2
}
三、计算类实例化个数

通过静态成员变量和构造函数/析构函数的配合,可以统计类的实例数量。

完整实现:

class InstanceTracker {
private:
    static int totalInstances;  // 总实例数
    static int currentInstances;  // 当前存在的实例数
    
    const int id;  // 实例唯一标识
    
public:
    InstanceTracker() : id(++totalInstances) {
        currentInstances++;
        cout << "创建实例 #" << id << endl;
    }
    
    ~InstanceTracker() {
        currentInstances--;
        cout << "销毁实例 #" << id << endl;
    }
    
    // 静态成员函数:获取统计信息
    static int getTotalInstances() { return totalInstances; }
    static int getCurrentInstances() { return currentInstances; }
    
    // 非静态成员函数:获取实例ID
    int getId() const { return id; }
};

// 初始化静态成员变量
int InstanceTracker::totalInstances = 0;
int InstanceTracker::currentInstances = 0;

int main() {
    cout << "初始状态:"
         << "总实例=" << InstanceTracker::getTotalInstances()
         << ", 当前实例=" << InstanceTracker::getCurrentInstances() << endl;
    
    {
        InstanceTracker obj1;
        InstanceTracker obj2;
        cout << "局部作用域内:"
             << "总实例=" << InstanceTracker::getTotalInstances()
             << ", 当前实例=" << InstanceTracker::getCurrentInstances() << endl;
    }  // obj1和obj2在此处析构
    
    cout << "局部作用域结束后:"
         << "总实例=" << InstanceTracker::getTotalInstances()
         << ", 当前实例=" << InstanceTracker::getCurrentInstances() << endl;
    
    InstanceTracker obj3;
    cout << "创建新实例后:"
         << "总实例=" << InstanceTracker::getTotalInstances()
         << ", 当前实例=" << InstanceTracker::getCurrentInstances() << endl;
    
    return 0;
}

输出结果:

初始状态:总实例=0, 当前实例=0
创建实例 #1
创建实例 #2
局部作用域内:总实例=2, 当前实例=2
销毁实例 #2
销毁实例 #1
局部作用域结束后:总实例=2, 当前实例=0
创建实例 #3
创建新实例后:总实例=3, 当前实例=1
销毁实例 #3
四、静态成员的作用域与可见性
  1. 类作用域:静态成员属于类,即使无对象也可访问

    class Math {
    public:
        static const double PI;  // 类内声明
        static double circleArea(double r) { return PI * r * r; }
    };
    
    const double Math::PI = 3.14159;  // 类外定义
    
    // 使用方式
    double area = Math::circleArea(5.0);
    
  2. 文件作用域:使用static关键字限制全局变量/函数的可见性(C++17前)

    // file1.cpp
    static int fileLocalVar = 10;  // 仅在file1.cpp可见
    
    // file2.cpp
    // 无法访问fileLocalVar
    
  3. 命名空间作用域:推荐使用namespace替代全局static

    namespace MyLib {
        static int helperFunc() { /*...*/ }  // 命名空间内可见
    }
    
五、静态成员与继承
  1. 基类与派生类共享静态成员

    class Base {
    public:
        static int count;
    };
    
    class Derived : public Base {};
    
    int Base::count = 0;  // 基类和派生类共享同一静态变量
    
    int main() {
        Base::count++;  // 基类修改
        Derived::count++;  // 派生类修改
        cout << Base::count << endl;  // 输出2
    }
    
  2. 静态成员函数可被继承和重写(隐藏)

    class Base {
    public:
        static void print() { cout << "Base" << endl; }
    };
    
    class Derived : public Base {
    public:
        static void print() { cout << "Derived" << endl; }  // 隐藏基类函数
    };
    
    int main() {
        Base::print();  // 输出Base
        Derived::print();  // 输出Derived
    }
    
六、静态成员的高级应用
  1. 单例模式(Singleton)

    class Singleton {
    private:
        static Singleton* instance;
        Singleton() = default;  // 私有构造函数
        Singleton(const Singleton&) = delete;  // 禁用拷贝
        
    public:
        static Singleton* getInstance() {
            if (instance == nullptr) {
                instance = new Singleton();
            }
            return instance;
        }
    };
    
    Singleton* Singleton::instance = nullptr;  // 初始化静态指针
    
  2. 静态常量成员

    class Config {
    public:
        static const int MAX_SIZE = 100;  // 整型常量可直接类内初始化
        static const double VERSION;  // 非整型需类外定义
    };
    
    const double Config::VERSION = 1.0;  // 类外定义
    
  3. 静态成员作为模板参数

    template<int N>
    class Array {
    private:
        int data[N];
    public:
        static int size() { return N; }
    };
    
    // 使用方式
    Array<10> arr;
    cout << arr.size() << endl;  // 输出10
    
七、注意事项与常见误区
  1. 静态成员必须类外定义

    class Bad {
        static int value;  // 仅声明,未定义
    };
    
    int main() {
        cout << Bad::value << endl;  // 链接错误:未定义的符号
    }
    
  2. 静态成员与多线程

    • 多线程环境下访问静态变量需加锁保护
    class ThreadSafeCounter {
    private:
        static int count;
        static mutex mtx;
    public:
        static void increment() {
            lock_guard<mutex> lock(mtx);
            count++;
        }
    };
    
  3. 静态成员的初始化顺序

    • 不同编译单元的静态对象初始化顺序未定义,可能导致依赖问题
    • 建议使用函数内静态变量(Meyers’ Singleton)
    class Logger {
    public:
        static Logger& getInstance() {
            static Logger instance;  // 线程安全的局部静态变量
            return instance;
        }
    };
    
八、静态成员总结
特性静态成员变量静态成员函数
存储位置全局数据区代码区(与普通函数相同)
作用域类作用域类作用域
this指针
访问权限可通过类名或对象访问可通过类名或对象访问
初始化类外初始化无需初始化
继承特性基类与派生类共享可被继承和隐藏

静态成员是C++中实现全局状态管理、资源共享和工具类的重要机制,合理使用可以提高代码的模块化程度和执行效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

鸥梨菌Honevid

心想事成

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

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

打赏作者

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

抵扣说明:

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

余额充值