c++ - 我的循环链表中的 remove 方法是否定义明确?

标签 c++ linked-list destructor doubly-linked-list circular-list

我正在构建一个循环链表,我想知道 do_remove 方法是否定义良好。当我运行该程序时,它向我显示它是,但是,我仍然有点困惑为什么在这种情况下我不需要虚拟析构函数。

只有当我想通过它的基指针销毁派生类时才需要虚拟析构函数吗?

这是否意味着通过将基类向下转换为派生类,然后调用派生类的析构函数,它总是调用基类的析构函数?

do_remove 方法中是否可能存在泄漏?

PS:我需要对象的创建和销毁是一个两步过程——分配/调用构造函数/调用析构函数/释放;这就是我暂时使用 ::operator new 的原因。

这是我正在编写的代码的一个独立示例:

#include <iostream>

struct NodeBase {
    NodeBase * previous;
    NodeBase * next;

    NodeBase() noexcept
    : previous(this)
    , next(this) {
    }

    NodeBase(NodeBase * const previous, NodeBase * const next) noexcept
    : previous(previous)
    , next(next) {
    }

    ~NodeBase() {
        std::puts("~NodeBase()");
    }
};

template <typename TYPE>
struct Node : NodeBase {
    TYPE data;

    template <typename ...ARGUMENTS>
    Node(NodeBase * const previous, NodeBase * const next, ARGUMENTS && ...arguments)
    : NodeBase(previous, next)
    , data(std::forward<ARGUMENTS>(arguments)...) {
        previous->next = this;
        next->previous = this;
    }

    ~Node() {
        std::puts("~Node()");
    }
};

template <typename TYPE>
class List {
    using Node = Node<TYPE>;

    int64_t this_length;
    NodeBase this_sentinel;

    Node * as_node(NodeBase * const input) noexcept {
        return static_cast<Node * const>(input);
    }

    Node const * as_node(NodeBase const * const input) const noexcept {
        return static_cast<Node const * const>(input);
    }

    template <typename ...ARGUMENTS>
    List & do_insert(NodeBase * const node, ARGUMENTS && ...arguments) {
        void * const address = ::operator new(sizeof(Node));
        try {
            new (address) Node(node->previous, node, std::forward<ARGUMENTS>(arguments)...);
            ++this_length;
            return *this;
        } catch (...) {
            ::operator delete(address);
            throw;
        }
    }

    // Is this method well defined?
    List & do_remove(NodeBase * input) noexcept {
        Node * const node = as_node(input);
        input->previous->next = input->next;
        input->next->previous = input->previous;
        node->~Node();
        ::operator delete(node);
        --this_length;
        return *this;
    }

public:
    List()
    : this_length(0)
    , this_sentinel() {
    }

    ~List() {
        std::puts("~List()");
        while (this_length) {
            pop();
        }
    }

    TYPE & head() noexcept {
        return as_node(this_sentinel.next)->data;
    }

    TYPE const & head() const noexcept {
        return as_node(this_sentinel.next)->data;
    }

    TYPE & last() noexcept {
        return as_node(this_sentinel.previous)->data;
    }

    TYPE const & last() const noexcept {
        return as_node(this_sentinel.previous)->data;
    }

    template <typename ...ARGUMENTS>
    List & push(ARGUMENTS && ...arguments) {
        return do_insert(this_sentinel.next, std::forward<ARGUMENTS>(arguments)...);
    }

    List & pop() noexcept {
        return do_remove(this_sentinel.next);
    }
};

int main() {

    List<int> list;

    list.push(5).push(7).push(3);

    std::cout << list.head() << std::endl;
    std::cout << list.last() << std::endl;

    return 0;
}

最佳答案

Is the virtual destructor only needed when I want to destroy a derived class through it's base pointer?

是的。仅当您使用基指针删除对象(或者如果您使用 unique_ptr 来管理它)时,您才需要基中的虚拟析构函数。其他情况,如删除指向大多数派生类型的指针或使用 'shared_ptr' 管理基类不需要虚拟析构函数。

Does this mean that by downcasting a base class to a derived class, and then calling the derived class destructor, it will always call the base class destructor?

是的。需要派生类的析构函数来调用(基类和成员)子对象的析构函数。

Can there be possible leaks in the do_remove method?

不,如果 TYPE 的析构函数没有泄漏。为简单起见,最好使用普通的删除表达式

    delete node;

而不是写出无论如何都需要做的事情

    node->~Node();
    ::operator delete(node);

关于c++ - 我的循环链表中的 remove 方法是否定义明确?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47856990/

相关文章:

c++ - 将 GCC 以外的其他编译器附加到 Windows 上的 Clion IDE

java - 如何在任意位置以 O(1) 的时间进行插入和读取?

c++ - 基于元组值对 vector 进行排序

c++ - 在 For 循环 C++ 中创建线程

arrays - 树木: Linked Lists vs Arrays (Efficiency)

Java:如何使用虚拟节点或将节点标记为虚拟节点

kotlin - Kotlin 编程语言中的析构函数

c++ - QGestureRecognizer 自动被QGestureManager 销毁?

c++ - 局部作用域对象按什么顺序被破坏?

c++ - 如何在 C++ 中动态分配连续的二维数组?