MST

星途 面试题库

面试题:C++智能指针与内存分配的关系

在C++中,智能指针(如`std::unique_ptr`、`std::shared_ptr`、`std::weak_ptr`)是如何帮助管理动态分配的内存的?请举例说明每种智能指针在不同场景下的使用,以及它们在防止内存泄漏方面的机制。
49.9万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

std::unique_ptr

std::unique_ptr 是一种独占式智能指针,它拥有对所指向对象的唯一所有权。当 std::unique_ptr 被销毁时,它所指向的对象也会被自动销毁,从而防止内存泄漏。

场景及示例

适合于一个对象只应由一个所有者管理的场景,比如管理一个文件句柄。

#include <iostream>
#include <memory>

class File {
public:
    File(const char* filename) {
        std::cout << "Opening file: " << filename << std::endl;
    }
    ~File() {
        std::cout << "Closing file" << std::endl;
    }
};

int main() {
    std::unique_ptr<File> filePtr(new File("example.txt"));
    // 这里filePtr拥有File对象的唯一所有权
    // 当filePtr离开作用域时,File对象会被自动销毁
    return 0;
}

防止内存泄漏机制

std::unique_ptr 利用RAII(Resource Acquisition Is Initialization)原则,在构造时获取资源(分配内存),在析构时释放资源(释放内存)。由于它是独占式的,不存在多个指针指向同一对象导致重复释放或内存泄漏的问题。

std::shared_ptr

std::shared_ptr 允许多个指针共享对同一对象的所有权。它使用引用计数来跟踪有多少个 std::shared_ptr 指向同一个对象。当引用计数降为0时,对象会被自动销毁。

场景及示例

适用于多个对象需要共享同一块资源的场景,例如在实现共享数据结构时。

#include <iostream>
#include <memory>

class SharedData {
public:
    SharedData() {
        std::cout << "SharedData created" << std::endl;
    }
    ~SharedData() {
        std::cout << "SharedData destroyed" << std::endl;
    }
};

int main() {
    std::shared_ptr<SharedData> ptr1 = std::make_shared<SharedData>();
    std::shared_ptr<SharedData> ptr2 = ptr1; // ptr2和ptr1共享同一个SharedData对象
    std::cout << "Use count of ptr1: " << ptr1.use_count() << std::endl;
    std::cout << "Use count of ptr2: " << ptr2.use_count() << std::endl;
    // 当ptr1和ptr2离开作用域时,引用计数降为0,SharedData对象被销毁
    return 0;
}

防止内存泄漏机制

通过引用计数机制,每次复制 std::shared_ptr 时,引用计数增加;每次 std::shared_ptr 被销毁时,引用计数减少。当引用计数为0时,对象的内存被自动释放,避免了内存泄漏。

std::weak_ptr

std::weak_ptr 是一种弱引用,它指向由 std::shared_ptr 管理的对象,但不增加引用计数。它主要用于解决 std::shared_ptr 之间的循环引用问题,防止内存泄漏。

场景及示例

假设有两个类 AB,它们相互引用,可能会导致循环引用。

#include <iostream>
#include <memory>

class B;

class A {
public:
    std::weak_ptr<B> bPtr;
    ~A() {
        std::cout << "A destroyed" << std::endl;
    }
};

class B {
public:
    std::shared_ptr<A> aPtr;
    ~B() {
        std::cout << "B destroyed" << std::endl;
    }
};

int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();
    a->bPtr = b;
    b->aPtr = a;
    // 如果不使用weak_ptr,a和b之间会形成循环引用,导致内存泄漏
    // 这里a和b离开作用域时,它们指向的对象能正确销毁
    return 0;
}

防止内存泄漏机制

std::weak_ptr 不增加引用计数,因此不会干扰 std::shared_ptr 的引用计数机制。当 std::shared_ptr 的引用计数降为0时,对象会被销毁,即使有 std::weak_ptr 指向它。std::weak_ptr 可以通过 lock() 方法尝试获取一个 std::shared_ptr,如果对象还存在,lock() 会返回一个有效的 std::shared_ptr,否则返回一个空指针。这有助于在需要时安全地访问由 std::shared_ptr 管理的对象,同时避免循环引用导致的内存泄漏。