C++学习笔记-new关键字

new关键字是C++中用于在堆上动态分配内存的操作符。它不仅分配内存,还会调用对象的构造函数进行初始化。

new的基本用法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using String = std::string;

class Entity
{
private:
    String m_Name;
public:
    Entity() : m_Name("Unknown") {}
    Entity(const String& name) : m_Name(name) {}

    const String& GetName() const { return m_Name; }
};

int main()
{
    // 分配单个对象
    Entity* e = new Entity();
    std::cout << e->GetName() << std::endl;
    delete e;  // 必须手动释放
    
    // 带参数的构造
    Entity* e2 = new Entity("Player");
    std::cout << e2->GetName() << std::endl;
    delete e2;
}

new vs malloc

malloc(C语言风格,不推荐)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int main()
{
    // malloc只分配内存,不调用构造函数
    Entity* e1 = (Entity*)malloc(sizeof(Entity));
    // e1指向的内存未初始化,成员变量处于未定义状态
    
    // 需要手动调用构造函数(复杂且不安全)
    // new (e1) Entity();  // placement new
    
    free(e1);  // 使用free释放,不会调用析构函数
}

new(C++风格,推荐)

1
2
3
4
5
6
7
8
int main()
{
    // new既分配内存又调用构造函数
    Entity* e = new Entity();
    // e指向的对象已经完全初始化
    
    delete e;  // delete会调用析构函数然后释放内存
}

数组的动态分配

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main()
{
    // 分配基本类型数组
    int* numbers = new int[50];  // 分配50个int,200字节
    
    // 初始化数组
    for (int i = 0; i < 50; ++i)
    {
        numbers[i] = i;
    }
    
    delete[] numbers;  // 注意:数组要用delete[]
    
    // 分配对象数组
    Entity* entities = new Entity[10];  // 调用10次默认构造函数
    
    for (int i = 0; i < 10; ++i)
    {
        std::cout << entities[i].GetName() << std::endl;
    }
    
    delete[] entities;  // 调用10次析构函数
}

Placement New

placement new允许在指定的内存位置构造对象:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
int main()
{
    // 先分配一块内存
    char* buffer = new char[sizeof(Entity)];
    
    // 在指定位置构造对象
    Entity* e = new(buffer) Entity("Placed");
    std::cout << e->GetName() << std::endl;
    
    // 手动调用析构函数
    e->~Entity();
    
    // 释放原始内存
    delete[] buffer;
}

更实际的placement new示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <memory>

int main()
{
    // 分配一块可以容纳多个Entity的内存
    void* memory = new char[sizeof(Entity) * 3];
    
    // 在内存中的不同位置构造对象
    Entity* e1 = new(memory) Entity("First");
    Entity* e2 = new((char*)memory + sizeof(Entity)) Entity("Second");
    Entity* e3 = new((char*)memory + sizeof(Entity) * 2) Entity("Third");
    
    std::cout << e1->GetName() << std::endl;
    std::cout << e2->GetName() << std::endl;
    std::cout << e3->GetName() << std::endl;
    
    // 手动调用析构函数
    e1->~Entity();
    e2->~Entity();
    e3->~Entity();
    
    // 释放内存
    delete[] (char*)memory;
}

new的重载

可以重载new操作符来自定义内存分配行为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class CustomEntity
{
private:
    String m_Name;
public:
    CustomEntity(const String& name) : m_Name(name) {}
    
    // 重载new操作符
    void* operator new(size_t size)
    {
        std::cout << "Custom new called, size: " << size << std::endl;
        return malloc(size);
    }
    
    // 重载delete操作符
    void operator delete(void* ptr)
    {
        std::cout << "Custom delete called" << std::endl;
        free(ptr);
    }
    
    const String& GetName() const { return m_Name; }
};

int main()
{
    CustomEntity* e = new CustomEntity("Custom");  // 调用自定义new
    std::cout << e->GetName() << std::endl;
    delete e;  // 调用自定义delete
}

异常安全

new可能抛出异常,需要注意异常安全:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <new>

int main()
{
    try
    {
        // 如果内存不足,new会抛出std::bad_alloc异常
        Entity* e = new Entity("Test");
        
        // 使用对象...
        
        delete e;
    }
    catch (const std::bad_alloc& ex)
    {
        std::cout << "Memory allocation failed: " << ex.what() << std::endl;
    }
    
    // 使用nothrow版本,失败时返回nullptr而不抛异常
    Entity* e2 = new(std::nothrow) Entity("Safe");
    if (e2)
    {
        std::cout << e2->GetName() << std::endl;
        delete e2;
    }
    else
    {
        std::cout << "Memory allocation failed" << std::endl;
    }
}

现代C++替代方案

使用智能指针

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <memory>

int main()
{
    // 使用make_unique(推荐)
    auto e1 = std::make_unique<Entity>("Smart");
    std::cout << e1->GetName() << std::endl;
    // 自动释放,无需delete
    
    // 使用make_shared
    auto e2 = std::make_shared<Entity>("Shared");
    std::cout << e2->GetName() << std::endl;
    // 自动释放,无需delete
    
    // 数组版本
    auto numbers = std::make_unique<int[]>(50);
    for (int i = 0; i < 50; ++i)
    {
        numbers[i] = i;
    }
    // 自动释放
}

使用容器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <vector>

int main()
{
    // 使用vector代替动态数组
    std::vector<Entity> entities;
    entities.emplace_back("Entity1");
    entities.emplace_back("Entity2");
    entities.emplace_back("Entity3");
    
    for (const auto& entity : entities)
    {
        std::cout << entity.GetName() << std::endl;
    }
    // 自动管理内存
}

常见错误

1. 忘记delete

1
2
3
4
5
void BadFunction()
{
    Entity* e = new Entity();
    // 忘记delete e; 导致内存泄漏
}

2. 重复delete

1
2
3
4
5
6
void BadFunction()
{
    Entity* e = new Entity();
    delete e;
    delete e;  // 错误:重复删除
}

3. delete和delete[]混用

1
2
3
4
5
6
7
8
void BadFunction()
{
    Entity* entities = new Entity[10];
    delete entities;  // 错误:应该用delete[]
    
    Entity* e = new Entity();
    delete[] e;  // 错误:应该用delete
}

总结

  1. new的主要目的是在堆上分配内存,new返回一个指针
  2. new会调用对象的构造函数,这是与malloc的主要区别
  3. new分配的内存必须用delete手动释放,数组用delete[]
  4. 现代C++建议:优先使用智能指针(make_unique、make_shared)而不是裸指针
  5. placement new:可以在指定内存位置构造对象,用于高级内存管理
updatedupdated2025-09-202025-09-20