MST

星途 面试题库

面试题:C++智能指针与复杂数据结构中指针的交互应用

设计一个使用`std::unique_ptr`管理节点的双向链表结构。要求实现链表节点的插入、删除操作,并且在这些操作过程中,要正确处理`std::unique_ptr`所管理的指针,避免悬空指针和内存泄漏。同时,解释在这种复杂数据结构场景下,`std::unique_ptr`相比于原始指针的优势和可能面临的挑战,以及如何应对这些挑战。
17.5万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

双向链表结构实现

#include <memory>
#include <iostream>

// 双向链表节点结构
struct Node {
    int data;
    std::unique_ptr<Node> next;
    Node* prev;

    Node(int value) : data(value), next(nullptr), prev(nullptr) {}
};

// 双向链表类
class DoublyLinkedList {
private:
    std::unique_ptr<Node> head;
    Node* tail;

public:
    DoublyLinkedList() : head(nullptr), tail(nullptr) {}

    // 插入新节点到链表头部
    void insertAtHead(int value) {
        auto newNode = std::make_unique<Node>(value);
        if (head) {
            head->prev = newNode.get();
            newNode->next = std::move(head);
        } else {
            tail = newNode.get();
        }
        head = std::move(newNode);
    }

    // 插入新节点到链表尾部
    void insertAtTail(int value) {
        auto newNode = std::make_unique<Node>(value);
        if (tail) {
            tail->next = std::move(newNode);
            newNode->prev = tail;
            tail = newNode.get();
        } else {
            head = std::move(newNode);
            tail = head.get();
        }
    }

    // 删除指定值的节点
    void deleteNode(int value) {
        Node* current = head.get();
        while (current) {
            if (current->data == value) {
                if (current == head.get()) {
                    head = std::move(current->next);
                    if (head) {
                        head->prev = nullptr;
                    } else {
                        tail = nullptr;
                    }
                } else if (current == tail) {
                    tail = current->prev;
                    tail->next.reset();
                } else {
                    current->prev->next = std::move(current->next);
                    current->next->prev = current->prev;
                }
                return;
            }
            current = current->next.get();
        }
    }

    // 打印链表内容
    void printList() {
        Node* current = head.get();
        while (current) {
            std::cout << current->data << " ";
            current = current->next.get();
        }
        std::cout << std::endl;
    }
};

std::unique_ptr相比于原始指针的优势

  1. 自动内存管理std::unique_ptr在其生命周期结束时会自动释放所管理的内存,避免了手动释放内存可能导致的内存泄漏问题。在双向链表这种复杂数据结构中,节点的插入和删除操作频繁,手动管理内存容易出错,std::unique_ptr大大简化了内存管理流程。
  2. 所有权语义清晰std::unique_ptr明确表示对其所管理对象的唯一所有权,避免了多个指针指向同一对象导致的悬空指针和双重释放问题。在双向链表中,节点的移动(如插入和删除操作)涉及指针的转移,std::unique_ptr的所有权语义使得这种操作更加安全和可预测。

可能面临的挑战及应对方法

  1. 跨函数传递的复杂性:由于std::unique_ptr的唯一性,在函数间传递时需要使用std::move语义,这可能增加代码的复杂性。在双向链表操作中,例如在插入或删除节点时,需要确保std::unique_ptr的正确转移。应对方法是在编写函数时明确参数和返回值的所有权转移方式,合理使用std::move
  2. 与遗留代码的兼容性:如果项目中部分代码仍然使用原始指针,与std::unique_ptr混合使用可能会带来问题。例如,一些C风格的库函数可能要求原始指针作为参数。应对方法是在接口处进行适当的转换,将std::unique_ptr转换为原始指针传递给需要的函数,并确保在函数返回后std::unique_ptr仍然能正确管理内存。同时,逐步将遗留代码迁移到使用智能指针。