c++ 类和动态内存分配

在C++中,当类涉及动态内存分配时,需特别注意资源管理,以避免内存泄漏、悬空指针等问题。以下是关键点和示例代码:

核心原则

  1. 析构函数:负责释放动态分配的内存。
  2. 拷贝构造函数:实现深拷贝,复制数据而非指针。
  3. 拷贝赋值运算符:处理自赋值并实现深拷贝。
  4. 移动语义(C++11+):通过移动构造函数和移动赋值运算符提升性能。

示例:自定义字符串类

cpp 复制代码
#include <cstring>
#include <utility>

class MyString {
private:
    char* str;

public:
    // 构造函数
    explicit MyString(const char* s = "") {
        str = new char[std::strlen(s) + 1];
        std::strcpy(str, s);
    }

    // 析构函数
    ~MyString() {
        delete[] str;
    }

    // 拷贝构造函数(深拷贝)
    MyString(const MyString& other) {
        str = new char[std::strlen(other.str) + 1];
        std::strcpy(str, other.str);
    }

    // 拷贝赋值运算符(拷贝并交换)
    MyString& operator=(MyString other) {
        swap(other);
        return *this;
    }

    // 移动构造函数
    MyString(MyString&& other) noexcept : str(other.str) {
        other.str = nullptr;
    }

    // 移动赋值运算符
    MyString& operator=(MyString&& other) noexcept {
        if (this != &other) {
            delete[] str;
            str = other.str;
            other.str = nullptr;
        }
        return *this;
    }

    // 交换辅助函数
    void swap(MyString& other) noexcept {
        std::swap(str, other.str);
    }

    // 访问数据
    const char* c_str() const {
        return str;
    }
};

关键点解析

  1. 深拷贝与浅拷贝:

    • 默认拷贝操作复制指针值(浅拷贝),导致多个对象共享同一内存。
    • 需手动实现拷贝构造函数和赋值运算符,分配新内存并复制内容(深拷贝)。
  2. 拷贝并交换惯用法:

    • 赋值运算符通过传值调用拷贝构造函数生成临时对象。
    • 交换当前对象与临时对象的资源,临时对象析构时自动释放旧资源。
  3. 移动语义:

    • 移动操作"窃取"资源,避免不必要的深拷贝。
    • 移动后源对象置于有效但未定义状态(通常置空指针)。
  4. 异常安全:

    • 在分配新内存前不修改原对象状态,确保异常时对象仍有效。

使用智能指针简化管理

若使用 std::unique_ptr 管理资源,可减少手动内存管理,但需显式实现深拷贝:

cpp 复制代码
#include <memory>
#include <algorithm>

class MyArray {
private:
    std::unique_ptr<int[]> arr;
    size_t size;

public:
    MyArray(size_t s) : arr(std::make_unique<int[]>(s)), size(s) {}

    // 深拷贝构造函数
    MyArray(const MyArray& other) : arr(std::make_unique<int[]>(other.size)), size(other.size) {
        std::copy(&other.arr[0], &other.arr[size], &arr[0]);
    }

    // 拷贝赋值运算符(拷贝并交换)
    MyArray& operator=(MyArray other) {
        swap(other);
        return *this;
    }

    // 移动操作由 unique_ptr 自动处理
    MyArray(MyArray&&) noexcept = default;
    MyArray& operator=(MyArray&&) noexcept = default;

    void swap(MyArray& other) noexcept {
        std::swap(arr, other.arr);
        std::swap(size, other.size);
    }
};

总结

  • 手动管理内存时,必须实现析构函数、拷贝构造、拷贝赋值(移动操作可选但推荐)。
  • 使用智能指针 (如 std::unique_ptr)可自动管理释放,但深拷贝仍需手动实现。
  • 遵循规则:若定义了拷贝构造、拷贝赋值或析构函数,通常需同时考虑移动操作。

正确管理动态内存能有效避免资源泄漏和程序崩溃,是C++高效编程的关键技能。

相关推荐
Wang's Blog40 分钟前
Java 项目实战: 交友项目 - 项目:需求分析与滑动数据设计
java·开发语言·交友
前端 贾公子1 小时前
第 5 章:Codex 从可运行程序到可交付工具
java·前端·数据库
gz927cool1 小时前
Python 全栈应用最佳实践完整指南
开发语言·python·ai
道尔柯南1 小时前
C++ 内存管理详解:从 malloc/free 到 new/delete
c++·面试
東隅已逝,桑榆非晚1 小时前
AVL数实现
c++·笔记·学习·算法
CoderYanger1 小时前
Java EE 进阶:5. Spring IoC&DI(第三弹):Spring DI
java·开发语言·spring boot·spring·面试·java-ee·mvc
思想觉悟1 小时前
SEAndroid实战
android·linux·c++
AIFQuant1 小时前
Python期货行情API接入:股指、黄金、原油多品种数据管道实战
开发语言·python·金融·区块链·量化开发
echome8881 小时前
JavaScript async/await 错误处理:别让 Promise 静默失败
开发语言·前端·javascript·promise
运维行者_1 小时前
供应链安全成最大风险源,企业如何构建可控的运维体系?
运维·开发语言·网络·数据库·安全·云计算·流量监控