C++在构造函数中使用new时需要注意哪些问题

来源:PHP编程网作者:三上悠亚头衔:网络博主
导读:本期聚焦于小伙伴创作的《C++在构造函数中使用new时需要注意哪些问题》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C++在构造函数中使用new时需要注意哪些问题》有用,将其分享出去将是对创作者最好的鼓励。

C++构造函数中使用new的基础场景

在C++中,当我们需要在对象生命周期内持有动态分配的资源时,经常会在构造函数中使用new来申请内存。比如下面这个简单的字符串类,在构造时通过new分配字符数组空间:

C++在构造函数中使用new时需要注意哪些问题

#include <cstring>
#include <iostream>

class MyString {
private:
    char* data;
    int length;
public:
    // 构造函数中使用new分配内存
    MyString(const char* str = nullptr) {
        if (str == nullptr) {
            length = 0;
            data = new char[1];
            data[0] = '';
        } else {
            length = strlen(str);
            data = new char[length + 1];
            strcpy(data, str);
        }
    }
};

核心注意事项一:必须配套实现析构函数释放内存

构造函数中用new申请的内存,必须在对象销毁时释放,否则会造成内存泄漏。因此需要显式定义析构函数,在其中使用delete或者delete[]释放对应的资源:

~MyString() {
    if (data != nullptr) {
        delete[] data;  // 注意数组要用delete[],单个对象用delete
        data = nullptr;
    }
}

这里要注意,如果构造函数中使用的是new type[]申请数组,析构必须用delete[];如果是new type申请单个对象,析构用delete,二者不能混用,否则会导致未定义行为。

核心注意事项二:实现正确的拷贝构造函数

如果类中有动态分配的资源,编译器默认生成的拷贝构造函数只会做浅拷贝,也就是把指针的值复制过去,不会复制指针指向的内存。这样会导致两个对象的指针指向同一块内存,销毁时会被释放两次,引发程序崩溃:

// 错误的浅拷贝示例,编译器默认生成的拷贝构造就是这种逻辑
MyString s1("hello");
MyString s2 = s1;  // 此时s1.data和s2.data指向同一块内存
// 当s1和s2销毁时,同一块内存会被delete[]两次,程序崩溃

因此需要自己实现深拷贝的拷贝构造函数,为新对象分配独立的内存空间,复制原对象的内容:

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

核心注意事项三:重载赋值运算符实现深拷贝

和拷贝构造类似,默认的赋值运算符也是浅拷贝,同样会引发重复释放的问题。而且赋值操作还需要处理自赋值的情况,以及先释放旧资源再分配新资源,避免内存泄漏:

// 重载赋值运算符
MyString& operator=(const MyString& other) {
    // 处理自赋值情况,避免先释放自己的内存再拷贝导致错误
    if (this == &other) {
        return *this;
    }
    // 先释放当前对象的旧资源
    delete[] data;
    // 分配新资源并拷贝内容
    length = other.length;
    data = new char[length + 1];
    strcpy(data, other.data);
    return *this;
}

核心注意事项四:异常安全处理

如果new申请内存失败,会抛出std::bad_alloc异常。如果构造函数中申请了多个资源,前面的资源申请成功,后面的申请失败,需要保证已经申请的资源能被正确释放,避免内存泄漏:

class ResourceHolder {
private:
    int* res1;
    int* res2;
public:
    ResourceHolder() {
        res1 = new int(10);  // 先申请res1
        try {
            res2 = new int(20);  // 再申请res2,如果这里失败会抛异常
        } catch (...) {
            delete res1;  // 捕获异常,释放已经申请的res1,避免泄漏
            throw;  // 重新抛出异常
        }
    }
    ~ResourceHolder() {
        delete res1;
        delete res2;
    }
};

常见错误总结

  • 忘记实现析构函数,导致动态分配的内存无法释放,造成内存泄漏
  • 拷贝构造和赋值运算符没有实现深拷贝,导致多个对象共享同一块内存,重复释放崩溃
  • new和delete、new[]和delete[]混用,引发未定义行为
  • 赋值运算符没有处理自赋值场景,导致先释放自己的内存再拷贝出错
  • 构造函数中申请多个资源时没有做异常安全处理,部分资源申请失败时泄漏已申请资源

简化方案:使用智能指针管理资源

如果觉得手动管理内存容易出错,可以使用C++11引入的智能指针,比如std::unique_ptr或者std::shared_ptr来管理动态资源,智能指针会在对象销毁时自动释放管理的资源,不需要手动写析构函数,也能避免很多拷贝相关的问题:

#include <memory>
#include <cstring>

class SafeString {
private:
    std::unique_ptr<char[]> data;
    int length;
public:
    SafeString(const char* str = nullptr) {
        if (str == nullptr) {
            length = 0;
            data = std::make_unique<char[]>(1);
            data[0] = '';
        } else {
            length = strlen(str);
            data = std::make_unique<char[]>(length + 1);
            strcpy(data.get(), str);
        }
    }
    // 不需要手动写析构函数、拷贝构造、赋值运算符,unique_ptr会自动处理
};

C++构造函数new运算符内存泄漏拷贝构造函数赋值运算符修改时间:2026-07-09 19:45:28

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。