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;
}
ü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 :) –