C++基础整理(2)之 new / delete 关键字

C++基础整理(2)

注:整理一些突然学到的C++知识,随时mark一下
例如:忘记的关键字用法,新关键字,新数据结构



提示:本文为 C++ 中常用的 new 和 delete的用法和举例


一、new 关键字 in C++

   在C++中,new 是一个新的操作符,用于分配动态内存(存在堆区,不是栈区),在堆区开辟新的内存空间以分配对象。当使用 new 时,它会调用对象的构造函数来初始化新分配的内存,并返回指向新创建对象的指针( new 的返回结果是一个指针)。注意 new 完一个对象之后必须使用 delete 语句手动释放在堆区的相应对象的内存空间。

引入新关键字 new / delete 的原因:当使用【非基本的数据类型的对象(即自己定义的 class)】时,对象的创建过程会涉及到【构造函数】的调用,而在对象销毁时则又会调用【析构函数】。相比之下,而原C中的 malloc 和 free 作为库函数,其代码已经预先编译完成,因此无法将构造函数和析构函数的新功能应用于它们。因此 malloc 和 free 仅仅负责内存的分配和释放,而不涉及对象生命周期中的构造和析构过程。这也是new / delete 相比 malloc / free 的优势。

new主要有以下使用场景:

1、分配基本数据类型

代码示例:

int* ptr = new int;  // 分配一个整数  
*ptr = 100;           // 设置整数的值为42  
  
delete ptr;          // 释放内存

2、分配自定义的类的对象

假设现在有一个名为 MyClass 的类:

代码示例:

class MyClass {  
public:  
    MyClass(int value) : m_value(value) {}  
    ~MyClass() {}  
    void printValue() const { std::cout << m_value << std::endl; }  
private:  
    int m_value;  
};  

//
MyClass* obj = new MyClass(10);  // 分配一个MyClass对象,并初始化它  
obj->printValue();                // 输出10   
delete obj;                      // 释放内存

3、分配数组

可以使用 new 来新开辟一段数组的内存:

代码示例:

int* arr = new int[5];  // 分配一个包含5个整数的数组  
for (int i = 0; i < 5; ++i) {  
    arr[i] = i * 2;    // 设置数组元素的值  
}  
  
delete[] arr;          // 释放数组的内存

二、delete 关键字 in C++

  delete用于释放之前通过new操作符在堆上分配的内存。当你使用new创建一个对象时,该对象会一直占用内存,直到你使用delete释放它。

1、delete 的语法

delete有两种形式:

delete ptr;//用于释放单个对象或基本数据类型的内存。
delete[] arr;//用于释放通过new[]创建的数组的内存。

2、delete 和 delete[] 的行为差异

delete:用于单个对象,它只会调用该对象的析构函数一次,并释放该对象所占用的内存。

delete[]:用于数组,它会对数组中的每个元素调用析构函数,并释放整个数组所占用的内存。(指针数组例外,要特殊处理)

3、指针数组的特殊处理

指针数组是一个很多指针构成的数组,其元素是指针而非对象。当需要释放这样的数组时,需要先确保每个指针所指向的对象(如果有的话)都已经被正确地析构掉了,然后再释放指针数组本身的内存。

下面是一个【指针数组】配合 delete 的例子:

class MyClass {  
public:  
    MyClass(int value) : m_value(value) {  
        std::cout << "MyClass(" << value << ") constructed\n";  
    }  
    ~MyClass() {  
        std::cout << "MyClass(" << m_value << ") destructed\n";  
    }  
    void printValue() const {  
        std::cout << "Value: " << m_value << std::endl;  
    }  
private:  
    int m_value;  
};  
  
int main() {  
    // 创建一个包含3个MyClass指针的数组  
    MyClass** ptrArray = new MyClass*[3];  
  
    // 为每个指针分配一个MyClass对象,并初始化  
    for (int i = 0; i < 3; ++i) {  
        ptrArray[i] = new MyClass(i);  
    }  
  
    // 使用对象...  
    for (int i = 0; i < 3; ++i) {  
        ptrArray[i]->printValue();  
    }  
  
    // 释放每个MyClass对象  
    for (int i = 0; i < 3; ++i) {  
        delete ptrArray[i]; // 调用每个对象的析构函数  
        ptrArray[i] = nullptr; // 避免悬挂指针  
    }  
  
    // 释放指针数组本身的内存  
    delete[] ptrArray; // 不需要调用析构函数,因为ptrArray是指针的数组 ,这里绝对不能使用delete ptrArray,会报错 
    ptrArray = nullptr; // 避免悬挂指针  
  
    return 0;  
}

在这个例子中,我们首先创建了一个包含3个MyClass*类型元素的指针数组ptrArray。然后,我们为每个指针分配了一个MyClass对象,并初始化了它们。在释放内存之前,我们遍历了数组,并使用delete来释放每个MyClass对象(调用它们的析构函数)。最后,我们使用delete[]来释放指针数组ptrArray本身的内存。注意,因为ptrArray是一个指针数组,而不是对象数组,所以delete[]不会调用指针的析构函数(指针没有析构函数)。它只负责释放数组占用的内存。简单的说,delete 和 delete[] 不能互用。delete 删除/释放某个指针,而delete[] 删除/释放某个数组

4、一些问题和风险

(1)悬挂指针:使用delete释放内存后,最好将指针设置为nullptr。这样做可以防止出现悬挂指针(dangling pointer),即指向【已被释放的内存空间】的指针。尝尝试访问悬挂指针的代码将导致未定义行为,通常导致程序崩溃。

(2)内存泄漏:不释放这些 new 出来的内存可能会导致内存泄漏,这是一种常见的编程错误,它可能导致程序消耗越来越多的内存,最终耗尽系统资源,必须避免。

(3)必须配对使用new 和 delete,以及 new[] 和 delete[] 。混用它们(例如,用delete释放通过new[]分配的内存)也会导致未定义行为。

(4)当使用delete释放内存时,对象的析构函数将被自动调用。如果你的类有管理其他资源(如动态分配的内存、文件句柄等),确保在析构函数中释放这些资源。

(5)异常安全:在构造函数可能抛出异常的情况下,使用 new 可能会导致资源泄漏或其他问题。

(6)智能指针:在现代C++编程中,通常推荐使用智能指针(如unique_ptrshared_ptr,后续章节会讲解)来自动管理动态分配的内存。因为它们可以自动处理内存释放,减少内存泄漏和悬挂指针的风险。虽然 new 和 delete 提供了手动管理内存的能力,但在许多情况下,使用智能指针和RAII(资源获取即初始化)技术来自动管理资源更为安全和方便。

5、类成员函数出现=delete的情况

如果一个类的成员函数后面紧跟着= delete,那么这意味着这个函数被显式地删除了,也就是说,禁止调用该函数。

= delete 通常用于以下情况:

禁止类的拷贝构造或移动构造:如果不希望一个类被拷贝或移动,可以将其拷贝构造函数和移动构造函数后面加一句= delete。

代码示例

class NonCopyable {
public:
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
};

成员函数的“封印”:如果有一个成员函数,但在某个特定的类实现中,你不希望它被调用,可以将其设为= delete的。

代码示例

class MyClass {
public:
    void MyFunction() = delete;
};

有时,仅仅将成员函数设为私有的 private 并不足以明确表示不希望它被调用。使用= delete可以更加明确地表达意图。使用= delete的好处是,它会在编译时捕获到任何尝试调用该函数的代码的行为,从而避免了运行时的错误。

总结

  • 24
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值