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++高效编程的关键技能。

相关推荐
CodeAmaz7 分钟前
自定义限流方案(基于 Redis + 注解)
java·redis·限流·aop·自定义注解
FAREWELL0007512 分钟前
Lua学习记录(3) --- Lua中的复杂数据类型_table
开发语言·学习·lua
Felix_XXXXL20 分钟前
Plugin ‘mysql_native_password‘ is not loaded`
java·后端
IT北辰22 分钟前
Python实现居民供暖中暖气能耗数据可视化分析(文中含源码)
开发语言·python·信息可视化
韩立学长23 分钟前
【开题答辩实录分享】以《基于SpringBoot在线小说阅读平台》为例进行答辩实录分享
java·spring boot·后端
悟能不能悟30 分钟前
jsp怎么拿到url参数
java·前端·javascript
KWTXX30 分钟前
组合逻辑和时序逻辑的区别
java·开发语言·人工智能
高山上有一只小老虎33 分钟前
字符串字符匹配
java·算法
wjs202440 分钟前
Go 语言结构体
开发语言
程序猿小蒜44 分钟前
基于SpringBoot的企业资产管理系统开发与设计
java·前端·spring boot·后端·spring