developer tip

'빈'생성자 또는 소멸자는 생성 된 생성자와 동일한 작업을 수행합니까?

copycodes 2020. 10. 26. 08:06
반응형

'빈'생성자 또는 소멸자는 생성 된 생성자와 동일한 작업을 수행합니까?


다음과 같은 (장난감) C ++ 클래스가 있다고 가정합니다.

class Foo {
    public:
        Foo();
    private:
        int t;
};

소멸자가 정의되지 않았으므로 C ++ 컴파일러는 class에 대해 자동으로 생성해야합니다 Foo. 소멸자가 동적으로 할당 된 메모리를 정리할 필요가없는 경우 (즉, 컴파일러가 제공하는 소멸자에 합리적으로 의존 할 수 있음) 빈 소멸자를 정의합니다.

Foo::~Foo() { }

컴파일러가 생성 한 것과 같은 일을합니까? 빈 생성자는 Foo::Foo() { }어떻습니까?

차이점이 있다면 어디에 존재합니까? 그렇지 않은 경우 한 방법이 다른 방법보다 선호됩니까?


그것은 같은 일을 할 것입니다 (본질적으로 아무것도 아닙니다). 그러나 그것은 당신이 그것을 쓰지 않은 것과 같지 않습니다. 소멸자를 작성하려면 작업 기본 클래스 소멸자가 필요하기 때문입니다. 기본 클래스 소멸자가 비공개이거나 호출 할 수없는 다른 이유가있는 경우 프로그램에 결함이있는 것입니다. 이걸 고려하세요

struct A { private: ~A(); };
struct B : A { }; 

동적으로 생성 된 객체에 대해 delete를 호출하지 않거나 객체를 생성하지 않는 경우와 같이 B 유형 (즉, 유형 A의 암시 적으로)을 파괴 할 필요가없는 한 괜찮습니다. 첫 번째 장소. 그렇게하면 컴파일러가 적절한 진단을 표시합니다. 이제 명시 적으로 제공하면

struct A { private: ~A(); };
struct B : A { ~B() { /* ... */ } }; 

그 사람은 기본 클래스의 소멸자를 암시 적으로 호출하려고 시도하고 정의 시간에 이미 진단을 발생 ~B시킵니다.

소멸자의 정의와 멤버 소멸자에 대한 암시 적 호출을 중심으로하는 또 다른 차이점이 있습니다. 이 스마트 포인터 멤버 고려

struct C;
struct A {
    auto_ptr<C> a;
    A();
};

struct의 정의도 포함하는 파일 C의 A 생성자 정의에서 유형의 객체 가 생성 되었다고 가정 해 보겠습니다 . 이제 struct를 사용 하고 객체를 파괴해야하는 경우 컴파일러는 위의 경우와 같이 소멸자의 암시 적 정의를 제공합니다. 해당 소멸자는 auto_ptr 객체의 소멸자를 암시 적으로 호출합니다. 그리고 그것은 객체 를 가리키는 포인터를 삭제할 것입니다 - ! struct A의 생성자가 정의 된 파일에 나타납니다 ..cppCAACC.cpp

이것은 실제로 pimpl 관용구를 구현할 때 흔히 발생하는 문제입니다. 여기서 해결책은 소멸자를 추가 .cpp하고 구조체 C가 정의 된 파일에 소멸자의 빈 정의를 제공하는 것입니다 . 멤버의 소멸자를 호출 할 때 struct의 정의를 알고 C소멸자를 올바르게 호출 할 수 있습니다.

struct C;
struct A {
    auto_ptr<C> a;
    A();
    ~A(); // defined as ~A() { } in .cpp file, too
};

참고 boost::shared_ptr이 문제가되지 않습니다이 : 생성자가 특정한 방식으로 호출 될 때 그것은 대신 완전한 형태를 필요로한다.

현재 C ++에서 차이를 만드는 또 다른 점 memset은 사용자가 소멸자를 선언 한 객체 를 사용 하고 싶을 때 입니다. 이러한 유형은 더 이상 POD (일반 오래된 데이터)가 아니며 비트 복사가 허용되지 않습니다. 이 제한은 실제로 필요하지 않습니다. 다음 C ++ 버전에서는이 상황을 개선하여 다른 더 중요한 변경 사항이 적용되지 않는 한 이러한 유형을 비트 복사 할 수 있습니다.


당신이 생성자를 요청했기 때문에 : 글쎄, 이것들에 대해 똑같은 것이 사실입니다. 생성자에는 소멸자에 대한 암시 적 호출도 포함됩니다. auto_ptr과 같은 경우 이러한 호출 (실제로 런타임에 수행되지 않았더라도-여기에서 순수한 가능성이 이미 중요 함)은 소멸자와 동일한 해를 끼치고 생성자의 무언가가 throw 될 때 발생합니다. 그런 다음 컴파일러는 소멸자를 호출해야합니다. 회원의. 이 답변 은 기본 생성자의 암시 적 정의를 사용합니다.

또한 위의 소멸자에 대해 말한 가시성과 PODness도 마찬가지입니다.

초기화와 관련하여 한 가지 중요한 차이점이 있습니다. 사용자가 선언 한 생성자를 넣으면 형식은 더 이상 멤버의 값 초기화를받지 않으며 필요한 초기화를 수행하는 것은 생성자에게 달려 있습니다. 예:

struct A {
    int a;
};

struct B {
    int b;
    B() { }
};

이 경우 다음은 항상 참입니다.

assert(A().a == 0);

다음은 정의되지 않은 동작이지만 b초기화되지 않았기 때문에 (생성자가 생략했습니다). 값은 0 일 수 있지만 다른 이상한 값일 수도 있습니다. 이러한 초기화되지 않은 개체에서 읽으려고하면 정의되지 않은 동작이 발생합니다.

assert(B().b == 0);

이것은 또한이 구문을 사용하는 사실 new처럼, new A()(끝에 괄호를 주 -이 생략 된 경우 값 초기화하지 않고, 어떤 사용자가이를 초기화 할 수있는 생성자를 선언하지 있기 때문에, a초기화되지 않은 남아있을 것입니다).


I know I'm late in the discussion, nevertheless my experience says that the compiler behaves differently when facing an empty destructor compared to a compiler generated one. At least this is the case with MSVC++ 8.0 (2005) and MSVC++ 9.0 (2008).

When looking at the generated assembly for some code making use of expression templates, I realized that in release mode, the call to my BinaryVectorExpression operator + (const Vector& lhs, const Vector& rhs) was never inlined. (please don't pay attention to the exact types and operator signature).

To further diagnose the problem, I enabled the various Compiler Warnings That Are Off by Default. The C4714 warning is particularly interesting. It is emitted by the compiler when a function marked with __forceinline doesn't get inlined nonetheless.

I enabled the C4714 warning and I marked the operator with __forceinline and I could verify the compiler reports it was unable to inline the call to the operator.

Among the reasons described in the documentation, the compiler fails to inline a function marked with __forceinline for:

Functions returning an unwindable object by value when -GX/EHs/EHa is on

This is the case of my BinaryVectorExpression operator + (const Vector& lhs, const Vector& rhs). BinaryVectorExpression is returned by value and even though its destructor is empty, it makes this return value being considered as an unwindable object. Adding throw () to the destructor didn't help the compiler and I avoid using exception specifications anyway. Commenting out the empty destructor let the compiler fully inline the code.

The take-away is that from now, in every class, I write commented out empty destructors to let humans know the destructor does nothing on purpose, the very same way people comment out the empty exception specification `/* throw() */ to indicate that the destructor cannot throw.

//~Foo() /* throw() */ {}

Hope that helps.


The empty destructor that you defined out of class has similar semantics in most regards, but not in all.

Specifically, the implicitly defined destructor
1) is an inline public member (yours is not inline)
2) is denoted as a trivial destructor (necessary to make trivial types that can be in unions, yours cannot)
3) has an exception specification (throw(), yours does not)


예, 빈 소멸자는 자동 생성 된 소멸자와 동일합니다. 저는 항상 컴파일러가 자동으로 생성하도록했습니다. 비정상적인 작업을 수행 할 필요가없는 한 소멸자를 명시 적으로 지정할 필요는 없다고 생각합니다. 가상 또는 비공개로 설정합니다.


나는 일반적으로 가상 소멸자를 정의하는 것이 좋다고 말하는 것을 제외하고 David와 동의합니다.

virtual ~Foo() { }

가상 소멸자를 놓치면 Foo 클래스에서 상속받은 사람들이 소멸자가 호출되지 않는다는 것을 알지 못했기 때문에 메모리 누수가 발생할 수 있습니다!


나는 빈 선언을 넣는 것이 가장 좋다고 말하고 싶습니다. 그것은 미래의 유지 관리자에게 그것이 감독이 아니라고 말하고 실제로 기본 선언을 사용하기로 결정했습니다.


정의를 참조 할 수 있으므로 빈 정의는 괜찮습니다.

virtual ~GameManager() { };
빈 선언은 모양이 기만적으로 비슷합니다.
가상 ~ GameManager ();
그러나 가상 소멸자 오류에 대한 정의가 없습니다.
Undefined symbols:
  "vtable for GameManager", referenced from:
      __ZTV11GameManager$non_lazy_ptr in GameManager.o
      __ZTV11GameManager$non_lazy_ptr in Main.o
ld: symbol(s) not found

참고 URL : https://stackoverflow.com/questions/1025313/will-an-empty-constructor-or-destructor-do-the-same-thing-as-the-generated-one

반응형