C++学习笔记-如何创建/实例化对象

在C++中,有两种主要的方式来创建对象:在栈上创建和在堆上创建。理解这两种方式的区别和使用场景对于编写高效的C++代码非常重要。

栈上创建对象(推荐)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 e0;  // 在栈上创建对象
        std::cout << e0.GetName() << std::endl;
    }  // 栈上创建的对象离开作用域后会自动销毁
    
    // 带参数的构造
    Entity e1("Player");
    std::cout << e1.GetName() << std::endl;
}

栈上创建的特点:

  • 自动管理:离开作用域时自动调用析构函数
  • 速度快:栈分配比堆分配快得多
  • 内存安全:不会出现内存泄漏
  • 有限空间:栈空间有限,不适合大对象

堆上创建对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int main()
{
    Entity* e1 = new Entity();  // 在堆上创建对象
    std::cout << e1->GetName() << std::endl;
    delete e1;  // 堆上创建的对象需要手动销毁
    
    // 带参数的构造
    Entity* e2 = new Entity("Boss");
    std::cout << e2->GetName() << std::endl;
    delete e2;
}

堆上创建的特点:

  • 手动管理:需要手动调用delete释放内存
  • 灵活控制:可以控制对象的生命周期
  • 空间大:堆空间相对较大
  • 容易出错:可能导致内存泄漏

何时使用堆上创建

1. 对象太大,栈空间无法存储

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class LargeObject
{
private:
    int data[1000000];  // 大数组
public:
    LargeObject() {}
};

int main()
{
    // 对于大对象,栈可能无法容纳
    // LargeObject obj;  // 可能导致栈溢出
    
    // 应该在堆上创建
    LargeObject* obj = new LargeObject();
    delete obj;
}

2. 需要显式控制对象的生命周期

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class GameManager
{
private:
    Entity* m_Player;
public:
    GameManager()
    {
        m_Player = new Entity("Player");  // 游戏开始时创建
    }
    
    ~GameManager()
    {
        delete m_Player;  // 游戏结束时销毁
    }
    
    Entity* GetPlayer() { return m_Player; }
};

3. 多态和继承场景

 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
31
32
33
class Animal
{
public:
    virtual void MakeSound() = 0;
    virtual ~Animal() = default;
};

class Dog : public Animal
{
public:
    void MakeSound() override { std::cout << "Woof!" << std::endl; }
};

class Cat : public Animal
{
public:
    void MakeSound() override { std::cout << "Meow!" << std::endl; }
};

int main()
{
    // 多态需要使用指针
    Animal* animals[] = {
        new Dog(),
        new Cat()
    };
    
    for (Animal* animal : animals)
    {
        animal->MakeSound();
        delete animal;
    }
}

现代C++的替代方案

使用智能指针(推荐)

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

int main()
{
    // 使用unique_ptr自动管理内存
    std::unique_ptr<Entity> e1 = std::make_unique<Entity>("Smart");
    std::cout << e1->GetName() << std::endl;
    // 不需要手动delete,unique_ptr会自动管理
    
    // 使用shared_ptr共享所有权
    std::shared_ptr<Entity> e2 = std::make_shared<Entity>("Shared");
    {
        std::shared_ptr<Entity> e3 = e2;  // 共享所有权
        std::cout << e3->GetName() << std::endl;
    }  // e3销毁,但e2仍然有效
    std::cout << e2->GetName() << std::endl;
}

性能对比

 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 <chrono>

void StackAllocation()
{
    auto start = std::chrono::high_resolution_clock::now();
    
    for (int i = 0; i < 1000000; ++i)
    {
        Entity e;  // 栈分配
    }
    
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    std::cout << "Stack allocation: " << duration.count() << " microseconds" << std::endl;
}

void HeapAllocation()
{
    auto start = std::chrono::high_resolution_clock::now();
    
    for (int i = 0; i < 1000000; ++i)
    {
        Entity* e = new Entity();  // 堆分配
        delete e;
    }
    
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    std::cout << "Heap allocation: " << duration.count() << " microseconds" << std::endl;
}

最佳实践

1. 优先使用栈分配

1
2
3
// 好的做法
Entity player;
player.DoSomething();

2. 必要时使用智能指针

1
2
3
// 现代C++推荐做法
auto player = std::make_unique<Entity>("Player");
player->DoSomething();

3. 避免裸指针

1
2
3
// 避免这样做
Entity* player = new Entity("Player");
// ... 容易忘记delete

总结

  1. 在C++中你不应该经常使用new关键字,因为在堆上创建对象需要手动管理内存,容易导致内存泄漏
  2. 什么时候应该在堆上创建对象
    • 对象太大,栈空间无法存储
    • 需要显式控制对象的生命周期
    • 多态场景需要使用指针
  3. 现代C++建议:优先使用栈分配,必要时使用智能指针而不是裸指针
  4. 性能考虑:栈分配比堆分配快得多,应该优先考虑
updatedupdated2025-09-202025-09-20