我的循环链表中的 remove 方法是否定义明确?
Is the remove method from my Circular Linked List well defined?
我正在构建一个循环链表,我想知道 do_remove
方法是否定义良好。当我 运行 程序时,它告诉我它是,但是,我仍然有点困惑为什么在这种情况下我不需要虚拟析构函数。
只有当我想通过基指针销毁派生的 class 时才需要虚拟析构函数吗?
这是否意味着通过将基 class 向下转换为派生 class,然后调用派生 class 析构函数,它总是会调用基 class析构函数?
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?
是的。派生的析构函数 class 需要调用(基类和成员)子对象的析构函数。
Can there be possible leaks in the do_remove method?
不会,如果TYPE的析构函数没有泄漏。为简单起见,最好使用普通的删除表达式
delete node;
而不是写出无论如何都需要做的事情
node->~Node();
::operator delete(node);
我正在构建一个循环链表,我想知道 do_remove
方法是否定义良好。当我 运行 程序时,它告诉我它是,但是,我仍然有点困惑为什么在这种情况下我不需要虚拟析构函数。
只有当我想通过基指针销毁派生的 class 时才需要虚拟析构函数吗?
这是否意味着通过将基 class 向下转换为派生 class,然后调用派生 class 析构函数,它总是会调用基 class析构函数?
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?
是的。派生的析构函数 class 需要调用(基类和成员)子对象的析构函数。
Can there be possible leaks in the do_remove method?
不会,如果TYPE的析构函数没有泄漏。为简单起见,最好使用普通的删除表达式
delete node;
而不是写出无论如何都需要做的事情
node->~Node();
::operator delete(node);