C++ vector容器存储对象和存储指针的区别(vector对象、vector指针)(存储指针时推荐使用智能指针)

文章目录

      • **1. 内存管理**
        • - **存储对象**:
        • - **存储指针**:
      • **2. 生命周期控制**
        • - **存储对象**:
        • - **存储指针**:
      • **3. 性能差异**
        • - **存储对象**:
        • - **存储指针**:
      • **4. 使用场景**
        • - **选择存储对象的情况**:
        • - **选择存储指针的情况**:
      • **5. 注意事项**
        • - **存储指针的风险**:
        • - **推荐实践**:
          • - 使用智能指针(如 `std::unique_ptr`、`std::shared_ptr`)替代原始指针,自动管理内存:
      • **6. 总结对比表**
      • **7. 示例代码**
        • - **存储对象**:
        • - **存储指针**:

在 C++ 中, std::vector 容器存储对象(如 int、自定义类实例)和指针(如 int*MyClass*)时,存在显著差异,主要体现在 内存管理、生命周期控制、性能开销使用场景 上。以下是详细对比:


1. 内存管理

- 存储对象
  • vector 管理对象的完整生命周期。
  • vector 被销毁或调用 clear() 时,所有对象会自动调用析构函数,内存被释放。
  • 示例:
    std::vector<MyClass> vec;
    vec.push_back(MyClass()); // 存储对象
    vec.clear();              // 自动释放所有对象内存
    
- 存储指针
  • vector 仅管理指针本身的生命周期,不负责释放指针指向的对象
  • 需要手动遍历 vectordelete 每个指针指向的对象,否则会导致内存泄漏。
  • 示例:
    std::vector<MyClass*> vec;
    vec.push_back(new MyClass()); // 存储指针
    for (auto* ptr : vec) delete ptr; // 手动释放内存
    vec.clear();                    // 仅释放指针本身
    

2. 生命周期控制

- 存储对象
  • vector 的扩容(如 push_back 超过容量时)会复制或移动对象到新内存区域,原对象被析构。
  • 示例:
    std::vector<std::string> vec;
    vec.push_back("hello"); // 如果扩容,"hello" 会被复制到新内存
    
- 存储指针
  • vector 扩容时仅复制指针(地址),不会影响指针指向的对象
  • 指针指向的对象生命周期需独立管理,与 vector 无关。
  • 示例:
    std::vector<MyClass*> vec;
    MyClass* obj = new MyClass();
    vec.push_back(obj); // 存储指针
    // 即使 vec 扩容,obj 的内存地址不会改变
    

3. 性能差异

- 存储对象
  • 优点:直接存储对象,访问效率高(连续内存,缓存友好)。
  • 缺点:对象较大时,频繁扩容可能导致大量复制开销(需调用拷贝构造函数)。
  • 适合场景:对象较小或频繁访问,且不需要动态分配内存。
- 存储指针
  • 优点:避免对象复制开销(仅复制指针地址)。
  • 缺点:需要手动管理内存(new/delete),且指针访问可能因内存不连续导致缓存效率低。
  • 适合场景:对象较大或需要共享所有权(如多 vector 共同管理同一对象)。

4. 使用场景

- 选择存储对象的情况
  • 对象生命周期与 vector 绑定,无需共享。
  • 需要频繁访问或修改对象,且对象较小。
  • 示例:存储基本类型(intfloat)或小型结构体。
- 选择存储指针的情况
  • 对象需要动态分配(如通过 new 创建)。
  • 需要共享对象所有权(多个 vector 或其他容器指向同一对象)。
  • 对象较大,复制成本高。
  • 示例:存储多态对象(基类指针指向派生类实例)。

5. 注意事项

- 存储指针的风险
  1. 内存泄漏:忘记 delete 指针会导致内存泄漏。
  2. 悬空指针:若 vector 管理的指针指向的对象被提前释放,后续访问会导致未定义行为。
  3. 重复释放:如知识库 [1] 提到的 double free 错误(例如在类析构函数中调用 vector().swap() 释放指针)。
- 推荐实践
- 使用智能指针(如 std::unique_ptrstd::shared_ptr)替代原始指针,自动管理内存:
std::vector<std::unique_ptr<MyClass>> vec;
vec.push_back(std::make_unique<MyClass>());
// 不需要手动 delete,vec 析构时自动释放

6. 总结对比表

特性 存储对象 存储指针
内存管理 自动释放 需手动 delete
生命周期绑定 vector 绑定 独立于 vector
扩容开销 复制对象(调用拷贝/移动构造函数) 仅复制指针地址
访问效率 高(连续内存) 低(间接访问)
适用场景 对象小、生命周期短 对象大、需要共享所有权或动态分配
风险 无(除非自定义析构逻辑) 内存泄漏、悬空指针、重复释放

7. 示例代码

- 存储对象
#include 
struct MyClass {
    MyClass() { printf("Constructor\n"); }
    ~MyClass() { printf("Destructor\n"); }
};
int main() {
    std::vector<MyClass> vec;
    vec.push_back(MyClass()); // 对象被复制到 vector 中
    vec.clear();              // 自动调用析构函数
}
- 存储指针
#include 
struct MyClass {
    MyClass() { printf("Constructor\n"); }
    ~MyClass() { printf("Destructor\n"); }
};
int main() {
    std::vector<MyClass*> vec;
    vec.push_back(new MyClass()); // 手动分配内存
    for (auto* ptr : vec) delete ptr; // 必须手动释放
    vec.clear();
}

通过以上对比,可以根据具体需求选择存储对象或指针。在现代 C++ 中,优先推荐使用智能指针来避免手动内存管理的风险。

你可能感兴趣的:(C/C++,c++,开发语言)