0

순환 링크 된 목록을 작성 중이며 do_remove 메서드가 잘 정의되어 있는지 알고 싶습니다. 내가 프로그램을 실행할 때, 그러나 나는 이것이 가상 소멸자를 왜 필요로하지 않는지 조금 아직도 혼란 스럽다.순환 링크 된 목록의 제거 방법이 잘 정의되어 있습니까?

가상 소멸자는 기본 포인터를 통해 파생 클래스를 제거하려는 경우에만 필요합니까?

기본 클래스를 파생 클래스로 다운 캐스팅 한 다음 파생 클래스 소멸자를 호출하면 기본 클래스 소멸자가 항상 호출됩니다.

do_remove 메서드에서 누수가 발생할 수 있습니까?

추신 : 두 단계 프로세스 - 생성자 호출/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을 사용하여 관리하는 경우) base에 가상 소멸자가 필요합니다. 대부분의 파생 형식에 대한 포인터를 삭제하거나 'shared_ptr'을 기본으로 사용하여 관리하는 것과 같은 다른 경우에는 가상 소멸자가 필요하지 않습니다.

이 파생 클래스, 에 기본 클래스를 downcasting하고 파생 클래스의 소멸자를 호출하여, 그것은 항상 기본 클래스 소멸자를 호출 할 것이라는 점을 의미 하는가?

예. 파생 클래스의 소멸자는 (기본 및 멤버) 하위 객체의 소멸자를 호출해야합니다.

do_remove 메소드에서 누수가 발생할 수 있습니까?

아니요, 유형의 소멸자가 누출되지 않으면 아니오. 내가 '왜 편의를 위해 대신이 어쨌든 나는 전문 노드 할당을 계획하고있어

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

을 할 필요가 무엇을 쓰는 일반 삭제 표현을

delete node; 

를 사용하는 것이 좋습니다 것, 그건 m 분리 파괴. 나중에 새로운 할당자를 수용하도록 코드를 변경하는 것이 더 쉽습니다. 감사 :) –

관련 문제