0

循環リンクリストを構築していますが、do_removeメソッドが明確に定義されているかどうかを知りたいと思います。私はプログラムを実行すると、私には、この場合に仮想デストラクターが必要ない理由はまだ分かりません。サーキュラーリンクリストの削除メソッドは明確に定義されていますか?

仮想デストラクターは、派生クラスをベースポインターで破棄したい場合にのみ必要ですか?

これは、ベースクラスを派生クラスにダウンキャストし、派生クラスデストラクタを呼び出すと、常にベースクラスのデストラクタを呼び出すのでしょうか?

do_removeメソッドでリークが発生する可能性はありますか?

PS:オブジェクトの作成と破壊が2段階のプロセスでなければなりません - コンストラクタへの割り当て/呼び出し/ destructor/deallocationへの呼び出し。そのため私は当分::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; 
} 

答えて

1

は、私はそれのベースポインタを通じて派生 クラスを破棄したい場合にのみ必要な仮想デストラクタですか?

はい。ベースポインタを使用してオブジェクトを削除した場合(またはベースにunique_ptrを使用して管理している場合)のみ、ベースに仮想デストラクタが必要です。ほとんどの派生型へのポインタの削除や 'shared_ptr'での管理のような他の場合は、仮想デストラクタは必要ありません。

これは派生クラス、 に基本クラスをダウンキャストして、派生クラスのデストラクタを呼び出すことによって、それは常に 基底クラスのデストラクタを呼び出すことを意味していますか?

はい。派生クラスのデストラクタは、(基本およびメンバ)サブオブジェクトのデストラクタを呼び出す必要があります。

do_removeメソッドでリークが発生する可能性はありますか?

いいえ、TYPEのデストラクタがリークしない場合、いいえ。私は「なぜ簡単にするために、それは通常のdelete式

delete node; 

の代わりに、とにかく、私は専門のノードアロケータを作ることに計画してい

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

行うために必要なもの書き出すを使用する方が良いだろう、それはです破壊と解放を分ける。新しいアロケータに対応するためにコードを後で変更するのが簡単になります。ありがとう:) –

関連する問題