developer tip

언제 가상 소멸자를 사용하지 말아야합니까?

optionbox 2020. 9. 1. 07:27
반응형

언제 가상 소멸자를 사용하지 말아야합니까?


클래스에 대해 가상 소멸자를 선언 하지 않는 합당한 이유가 있습니까? 구체적으로 작성을 피해야 할 때는 언제입니까?


아래에 해당하는 경우 가상 소멸자를 사용할 필요가 없습니다.

  • 클래스를 파생시킬 의도가 없습니다.
  • 힙에서 인스턴스화하지 않음
  • 슈퍼 클래스의 포인터에 저장할 의도가 없습니다.

당신이 정말로 기억에 눌리지 않는 한 그것을 피할 특별한 이유가 없습니다.


질문에 명시 적으로 대답하기 위해, 즉 언제 가상 소멸자를 선언 하지 않아야합니다 .

C ++ '98 / '03

가상 소멸자를 추가하면 클래스가 POD (일반 이전 데이터) * 또는 집계에서 비 POD로 변경 될 수 있습니다 . 클래스 유형이 어딘가에서 초기화 된 경우 프로젝트 컴파일이 중지 될 수 있습니다.

struct A {
  // virtual ~A ();
  int i;
  int j;
};
void foo () { 
  A a = { 0, 1 };  // Will fail if virtual dtor declared
}

극단적 인 경우 이러한 변경으로 인해 클래스가 POD가 필요한 방식으로 사용되는 정의되지 않은 동작 (예 : 생략 매개 변수를 통해 전달 또는 memcpy와 함께 사용)이 발생할 수도 있습니다.

void bar (...);
void foo (A & a) { 
  bar (a);  // Undefined behavior if virtual dtor declared
}

[* POD 유형은 메모리 레이아웃에 대해 특정 보증이있는 유형입니다. 표준은 실제로 POD 유형의 객체에서 문자 배열 (또는 부호없는 문자)로 복사하고 다시 되 돌리는 경우 결과는 원래 객체와 동일하다고 말합니다.]

최신 C ++

최신 버전의 C ++에서 POD의 개념은 클래스 레이아웃과 구성, 복사 및 파괴로 나뉘어졌습니다.

줄임표의 경우 더 이상 정의되지 않은 동작이 아니며 구현 정의 의미 체계 (N3937-~ C ++ '14-5.2.2 / 7)로 조건부로 지원됩니다.

... 사소하지 않은 복사 생성자, 사소하지 않은 이동 생성자 또는 해당 매개 변수가없는 사소한 소멸자를 갖는 클래스 유형 (Clause 9)의 잠재적으로 평가 된 인수를 전달하는 것은 구현시 조건부로 지원됩니다. 정의 된 의미론.

=defaultwill 이외의 소멸자를 선언하면 사소하지 않습니다 (12.4 / 5).

... 소멸자는 사용자가 제공하지 않으면 사소합니다 ...

Modern C ++의 다른 변경 사항은 생성자를 추가 할 수 있으므로 집계 초기화 문제의 영향을 줄입니다.

struct A {
  A(int i, int j);
  virtual ~A ();
  int i;

  int j;
};
void foo () { 
  A a = { 0, 1 };  // OK
}

가상 메서드가있는 경우에만 가상 소멸자를 선언합니다. 가상 메서드가 있으면 힙에서 인스턴스화하거나 기본 클래스에 대한 포인터를 저장하는 것을 피할 수 있다고 믿지 않습니다. 둘 다 매우 일반적인 작업이며 소멸자가 가상으로 선언되지 않은 경우 자동으로 리소스가 누출되는 경우가 많습니다.


가상 소멸자는 delete클래스 유형을 사용하여 하위 클래스의 개체에 대한 포인터에서 호출 될 수 있는 기회가 있을 때마다 필요 합니다. 이렇게하면 컴파일러가 컴파일 타임에 힙에있는 개체의 클래스를 알 필요없이 런타임에 올바른 소멸자가 호출되도록합니다. 예를 들어 다음과 B같은 하위 클래스 라고 가정합니다 A.

A *x = new B;
delete x;     // ~B() called, even though x has type A*

코드가 성능에 중요하지 않은 경우 안전을 위해 작성하는 모든 기본 클래스에 가상 소멸자를 추가하는 것이 합리적입니다.

However, if you found yourself deleteing a lot of objects in a tight loop, the performance overhead of calling a virtual function (even one that's empty) might be noticeable. The compiler cannot usually inline these calls, and the processor might have a difficult time predicting where to go. It is unlikely this would have a significant impact on performance, but it's worth mentioning.


Virtual functions mean every allocated object increases in memory cost by a virtual function table pointer.

So if your program involves allocating a very large number of some object, it would be worth avoiding all virtual functions in order to save the additional 32 bits per object.

In all other cases, you will save yourself debug misery to make the dtor virtual.


Not all C++ classes are suitable for use as a base class with dynamic polymorphism.

If you want your class to be suitable for dynamic polymorphism, then its destructor must be virtual. In addition, any methods which a subclass could conceivably want to override (which might mean all public methods, plus potentially some protected ones used internally) must be virtual.

If your class is not suitable for dynamic polymorphism, then the destructor should not be marked virtual, because to do so is misleading. It just encourages people to use your class incorrectly.

Here's an example of a class which would not be suitable for dynamic polymorphism, even if its destructor were virtual:

class MutexLock {
    mutex *mtx_;
public:
    explicit MutexLock(mutex *mtx) : mtx_(mtx) { mtx_->lock(); }
    ~MutexLock() { mtx_->unlock(); }
private:
    MutexLock(const MutexLock &rhs);
    MutexLock &operator=(const MutexLock &rhs);
};

The whole point of this class is to sit on the stack for RAII. If you're passing around pointers to objects of this class, let alone subclasses of it, then you're Doing It Wrong.


A good reason for not declaring a destructor as virtual is when this saves your class from having a virtual function table added, and you should avoid that whenever possible.

I know that many people prefer to just always declare destructors as virtual, just to be on the safe side. But if your class does not have any other virtual functions then there is really, really no point in having a virtual destructor. Even if you give your class to other people who then derive other classes from it then they would have no reason to ever call delete on a pointer that was upcast to your class - and if they do then I would consider this a bug.

Okay, there is one single exception, namely if your class is (mis-)used to perform polymorphic deletion of derived objects, but then you - or the other guys - hopefully know that this requires a virtual destructor.

Put another way, if your class has a non-virtual destructor then this is a very clear statement: "Don't use me for deleting derived objects!"


If you have a very small class with a huge number of instances, the overhead of a vtable pointer can make a difference in your program's memory usage. As long as your class doesn't have any other virtual methods, making the destructor non-virtual will save that overhead.


I usually declare the destructor virtual, but if you have performance critical code that is used in an inner loop, you might want to avoid the virtual table lookup. That can be important in some cases, like collision checking. But be careful about how you destroy those objects if you use inheritance, or you will destroy only half of the object.

Note that the virtual table lookup happens for an object if any method on that object is virtual. So no point in removing the virtual specification on a destructor if you have other virtual methods in the class.


If you absolutely positively must ensure that your class does not have a vtable then you must not have a virtual destructor as well.

This is a rare case, but it does happen.

The most familiar example of a pattern that does this are the DirectX D3DVECTOR and D3DMATRIX classes. These are class methods instead of functions for the syntactic sugar, but the classes intentionally do not have a vtable in order to avoid the function overhead because these classes are specifically used in the inner loop of many high-performance applications.


On operation that will be performed on the base class, and that should behave virtually, should be virtual. If deletion can be performed polymorphically through the base class interface, then it must behave virtually and be virtual.

The destructor has no need to be virtual if you don't intend to derive from the class. And even if you do, a protected non-virtual destructor is just as good if deletion of base class pointers isn't required.


The performance answer is the only one I know of which stands a chance of being true. If you've measured and found that de-virtualizing your destructors really speeds things up, then you've probably got other things in that class that need speeding up too, but at this point there are more important considerations. Some day someone is going to discover that your code would provide a nice base class for them and save them a week's work. You'd better make sure they do that week's work, copying and pasting your code, instead of using your code as a base. You'd better make sure you make some of your important methods private so that no one can ever inherit from you.

참고URL : https://stackoverflow.com/questions/300986/when-should-you-not-use-virtual-destructors

반응형