MST

星途 面试题库

面试题:C++智能指针与类型安全性的关系

C++ 引入了智能指针(如std::unique_ptr、std::shared_ptr和std::weak_ptr)来管理动态内存。请解释智能指针如何与类型安全性相互关联,例如在对象生命周期管理和避免悬空指针等方面,以及它们在复杂继承体系下如何保证类型安全。
25.1万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

智能指针与类型安全性的关联

  1. 对象生命周期管理
    • std::unique_ptr
      • 它采用独占所有权模型。当std::unique_ptr离开其作用域时,它所指向的对象会被自动销毁。例如:
#include <memory>
class MyClass {
public:
    MyClass() { std::cout << "MyClass constructor" << std::endl; }
    ~MyClass() { std::cout << "MyClass destructor" << std::endl; }
};
int main() {
    std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();
    // 当ptr离开作用域,MyClass对象会被自动销毁
    return 0;
}
 - 这种自动销毁机制确保了对象生命周期的正确管理,避免了手动释放内存可能导致的内存泄漏,从内存管理角度保证了类型安全。
  • std::shared_ptr
    • 它采用引用计数模型。多个std::shared_ptr可以指向同一个对象,每当一个std::shared_ptr被创建或赋值时,引用计数增加;当std::shared_ptr离开作用域或被重置时,引用计数减少。当引用计数变为0时,对象被自动销毁。例如:
#include <memory>
class MyClass {
public:
    MyClass() { std::cout << "MyClass constructor" << std::endl; }
    ~MyClass() { std::cout << "MyClass destructor" << std::endl; }
};
int main() {
    std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
    std::shared_ptr<MyClass> ptr2 = ptr1;
    // 此时引用计数为2
    {
        std::shared_ptr<MyClass> ptr3 = ptr1;
        // 引用计数变为3
    }
    // ptr3离开作用域,引用计数变回2
    // 当ptr1和ptr2离开作用域,引用计数变为0,MyClass对象被销毁
    return 0;
}
 - 这种机制同样保证了对象生命周期的正确管理,减少了手动管理内存的错误,提升了类型安全。

2. 避免悬空指针

  • std::unique_ptr和std::shared_ptr
    • 由于它们会自动管理对象的销毁,当对象被销毁时,指向该对象的智能指针会被正确更新(std::unique_ptr变为空指针,std::shared_ptr引用计数减少,当变为0时对象销毁)。不会出现普通指针在对象被手动释放后仍然指向已释放内存的悬空指针情况。例如:
#include <memory>
class MyClass {
public:
    MyClass() { std::cout << "MyClass constructor" << std::endl; }
    ~MyClass() { std::cout << "MyClass destructor" << std::endl; }
};
int main() {
    std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();
    MyClass* rawPtr = ptr.get();
    ptr.reset();
    // rawPtr此时成为悬空指针,但ptr是安全的,不会导致野指针访问
    return 0;
}
  • std::weak_ptr
    • 它是一种弱引用,不增加对象的引用计数。主要用于解决std::shared_ptr的循环引用问题。它可以指向由std::shared_ptr管理的对象,但当std::shared_ptr的引用计数变为0,对象被销毁时,std::weak_ptr会自动变为空指针。例如:
#include <memory>
class B;
class A {
public:
    std::weak_ptr<B> bPtr;
    ~A() { std::cout << "A destructor" << std::endl; }
};
class B {
public:
    std::weak_ptr<A> aPtr;
    ~B() { std::cout << "B destructor" << 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;
    // 这里没有循环引用问题
    // 当a和b离开作用域,A和B对象会被正确销毁,aPtr和bPtr会变为空指针
    return 0;
}
  1. 复杂继承体系下保证类型安全
    • 向上转型
      • 智能指针支持向上转型,就像普通指针一样。例如,假设有一个继承体系class Derived : public Base,可以将std::unique_ptr<Derived>std::shared_ptr<Derived>转换为std::unique_ptr<Base>std::shared_ptr<Base>。例如:
#include <memory>
class Base {
public:
    virtual ~Base() = default;
};
class Derived : public Base {
};
int main() {
    std::unique_ptr<Derived> derivedPtr = std::make_unique<Derived>();
    std::unique_ptr<Base> basePtr = std::move(derivedPtr);
    // 类型安全的向上转型
    return 0;
}
  • 向下转型
    • 使用std::dynamic_pointer_cast进行安全的向下转型。它与dynamic_cast类似,但是用于智能指针。如果转型成功,会返回一个有效的智能指针;如果失败,会返回一个空指针。例如:
#include <memory>
class Base {
public:
    virtual ~Base() = default;
};
class Derived : public Base {
};
int main() {
    std::shared_ptr<Base> basePtr = std::make_shared<Derived>();
    std::shared_ptr<Derived> derivedPtr = std::dynamic_pointer_cast<Derived>(basePtr);
    if (derivedPtr) {
        // 转型成功
    } else {
        // 转型失败
    }
    return 0;
}
  • 这样在复杂继承体系中,智能指针通过这些机制保证了在对象转换过程中的类型安全,避免了因错误的指针类型转换导致的未定义行为。