在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
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;
}
|
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; }
};
|
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;
}
}
|
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
2
3
|
// 好的做法
Entity player;
player.DoSomething();
|
1
2
3
|
// 现代C++推荐做法
auto player = std::make_unique<Entity>("Player");
player->DoSomething();
|
1
2
3
|
// 避免这样做
Entity* player = new Entity("Player");
// ... 容易忘记delete
|
- 在C++中你不应该经常使用new关键字,因为在堆上创建对象需要手动管理内存,容易导致内存泄漏
- 什么时候应该在堆上创建对象:
- 对象太大,栈空间无法存储
- 需要显式控制对象的生命周期
- 多态场景需要使用指针
- 现代C++建议:优先使用栈分配,必要时使用智能指针而不是裸指针
- 性能考虑:栈分配比堆分配快得多,应该优先考虑