0

Ich baue eine kreisförmige verkettete Liste, und ich würde gerne wissen, ob die do_remove Methode gut definiert ist. Wenn ich das Programm starte, zeigt es mir, dass es mich jedoch immer noch etwas verwirrt, warum ich in diesem Fall keinen virtuellen Destrutor brauche.Ist die Remove-Methode von meiner Circular Linked List gut definiert?

Wird der virtuelle Destruktor nur benötigt, wenn ich eine abgeleitete Klasse durch den Basiszeiger zerstören will?

Bedeutet dies, dass der Basisklasse-Destruktor immer aufgerufen wird, wenn eine Basisklasse in eine abgeleitete Klasse umgewandelt und dann der abgeleitete Klassendestruktor aufgerufen wird?

Kann es bei der do_remove Methode zu Undichtigkeiten kommen?

PS: Ich brauche die Objekterzeugung und -zerstörung als zweistufigen Prozess - Zuweisung/Aufruf an Konstruktor/Aufruf an Destruktor/Freigabe; deshalb verwende ich vorerst ::operator new.

Hier ist ein in sich geschlossenes Beispiel für den Code Ich schreibe:

#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; 
} 

Antwort

1

Ist die virtuelle destructor nur benötigt, wenn ich eine abgeleitete Klasse zerstören wollen durchBasisZeiger ist?

Ja. Nur wenn Sie ein Objekt mit dem Basiszeiger löschen (oder wenn Sie es mit unique_ptr zur Basis verwalten), benötigen Sie einen virtuellen Destruktor in der Basis. Andere Fälle, wie das Löschen des Zeigers auf den meisten abgeleiteten Typ oder das Verwalten mit 'shared_ptr' auf Basis, benötigen keinen virtuellen Destruktor.

Bedeutet dies, dass eine Basisklasse auf eine abgeleitete Klasse von einziehenden, und dann die abgeleitete Klasse destructor aufrufen, es wird die Basisklasse Destruktor immer anrufen?

Ja. Der Destruktor der abgeleiteten Klasse muss Destruktoren von (Basis- und Member-) Unterobjekten aufrufen.

Kann es zu möglichen Lecks in der do_remove-Methode kommen?

Nein, wenn der Destruktor von TYPE nicht leckt. Zur Vereinfachung wäre es besser, verwenden gewöhnlichen Lösch Ausdruck

delete node; 

statt zu schreiben, was es ohnehin zu tun

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

über die Schaffung eines spezialisierten Knoten allocator Ich plane erforderlich ist, das ist, warum ich m Trennung von Zerstörung und Deallokation. Es erleichtert die spätere Änderung des Codes für den neuen Zuordner. Vielen Dank :) –