MST
星途 面试题库

面试题:C++智能指针与多态指针的深度应用

在一个复杂的C++多态类继承体系中,如何合理使用智能指针(如std::unique_ptr、std::shared_ptr、std::weak_ptr)来管理对象生命周期,同时确保虚函数机制和动态类型转换(如dynamic_cast)的正常运作?请设计一个完整的类继承体系,并结合智能指针的使用,详细说明在不同场景下(如对象创建、对象销毁、对象转换等)的实现细节和注意事项。
18.8万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

类继承体系设计

假设我们有一个简单的图形类继承体系,包含基类 Shape 和派生类 CircleRectangle

#include <iostream>
#include <memory>

class Shape {
public:
    virtual void draw() const = 0;
    virtual ~Shape() = default;
};

class Circle : public Shape {
    double radius;
public:
    Circle(double r) : radius(r) {}
    void draw() const override {
        std::cout << "Drawing a Circle with radius " << radius << std::endl;
    }
};

class Rectangle : public Shape {
    double width, height;
public:
    Rectangle(double w, double h) : width(w), height(h) {}
    void draw() const override {
        std::cout << "Drawing a Rectangle with width " << width << " and height " << height << std::endl;
    }
};

对象创建

  1. std::unique_ptr
    • 适用于对象唯一拥有所有权的场景。
    • 创建 Circle 对象示例:
std::unique_ptr<Shape> createCircle() {
    return std::make_unique<Circle>(5.0);
}
  1. std::shared_ptr
    • 适用于多个对象可能共享所有权的场景。
    • 创建 Rectangle 对象示例:
std::shared_ptr<Shape> createRectangle() {
    return std::make_shared<Rectangle>(3.0, 4.0);
}

对象销毁

  1. std::unique_ptr
    • std::unique_ptr 离开作用域时,它所管理的对象会自动销毁。
    • 例如:
{
    std::unique_ptr<Shape> shape = createCircle();
    // 在这里使用 shape
} // shape 离开作用域,Circle 对象被销毁
  1. std::shared_ptr
    • 当最后一个指向对象的 std::shared_ptr 被销毁时,对象才会被销毁。
    • 例如:
{
    std::shared_ptr<Shape> shape1 = createRectangle();
    std::shared_ptr<Shape> shape2 = shape1; // 共享所有权
} // shape1 和 shape2 离开作用域,引用计数降为 0,Rectangle 对象被销毁

对象转换

  1. 使用 dynamic_cast 与智能指针
    • 对于 std::unique_ptr
std::unique_ptr<Shape> shape = createCircle();
std::unique_ptr<Circle> circlePtr = std::unique_ptr<Circle>(dynamic_cast<Circle*>(shape.release()));
if (circlePtr) {
    circlePtr->draw();
}
  • 对于 std::shared_ptr
std::shared_ptr<Shape> shape = createRectangle();
std::shared_ptr<Rectangle> rectanglePtr = std::dynamic_pointer_cast<Rectangle>(shape);
if (rectanglePtr) {
    rectanglePtr->draw();
}

注意事项

  1. 虚析构函数
    • 基类(如 Shape)必须有虚析构函数,这样在通过基类指针(智能指针也一样)销毁派生类对象时,才能正确调用派生类的析构函数。
  2. std::weak_ptr 的使用
    • 当存在循环引用可能导致内存泄漏时,可使用 std::weak_ptr。例如,假设 Shape 类有一个指向另一个 Shape 的成员指针,为避免循环引用:
class Shape {
public:
    virtual void draw() const = 0;
    virtual ~Shape() = default;
    std::weak_ptr<Shape> relatedShape;
};
  • 可通过 std::weak_ptr 获取 std::shared_ptr 来访问对象,但需先检查 std::weak_ptr 是否有效:
std::shared_ptr<Shape> otherShape = relatedShape.lock();
if (otherShape) {
    otherShape->draw();
}
  1. dynamic_cast 失败处理
    • dynamic_cast 对于无效转换会返回空指针(对于指针类型)或抛出异常(对于引用类型)。在使用 dynamic_cast 与智能指针时,要检查转换结果,避免空指针解引用。