나는 원래 이것을 소멸자에 대한 질문으로 만 게시했지만 이제 기본 생성자를 고려하고 있습니다. 원래 질문은 다음과 같습니다.
클래스에 가상의 소멸자를 제공하고 싶지만 컴파일러가 생성하는 것과 동일한 소멸자를 제공하려면
=default
다음을 사용할 수 있습니다 .class Widget { public: virtual ~Widget() = default; };
그러나 빈 정의를 사용하여 타이핑을 줄이면 동일한 효과를 얻을 수있는 것 같습니다.
class Widget { public: virtual ~Widget() {} };
이 두 정의가 다르게 행동하는 방법이 있습니까?
이 질문에 대한 답변을 기반으로 기본 생성자의 상황이 비슷해 보입니다. 소멸자에 대해 ” =default
“와 ” {}
“의 의미에 거의 차이가 없다는 것을 감안할 때 기본 생성자에 대한 이러한 옵션 사이에 의미에 거의 차이가 없습니까? 즉, 해당 유형의 객체가 생성되고 파괴되는 유형을 생성한다고 가정하면 왜 말하고 싶습니까?
Widget() = default;
대신에
Widget() {}
?
원래 게시물이 일부 SO 규칙을 위반 한 후이 질문을 확장하면 죄송합니다. 기본 생성자에 대해 거의 동일한 질문을 게시하면 바람직하지 않은 옵션으로 생각났습니다.
답변
이것은 소멸자와 생성자에 대해 요청할 때 완전히 다른 질문입니다.
만약 당신의 소멸자가 그렇다면 하워드가 지적한 것처럼virtual
그 차이는 무시할 만하다 . 그러나 소멸자가 가상 이 아닌 경우 완전히 다른 이야기입니다. 생성자도 마찬가지입니다.
= default
특수 멤버 함수 (기본 생성자, 복사 / 이동 생성자 / 할당, 소멸자 등)에 구문을 사용 한다는 것은 단순히하는 것과 매우 다른 의미 {}
입니다. 후자의 경우, 기능은 “사용자 제공”이됩니다. 그리고 그것은 모든 것을 바꿉니다.
이것은 C ++ 11의 정의에 의한 간단한 클래스입니다.
struct Trivial
{
int foo;
};
기본 생성을 시도하면 컴파일러에서 기본 생성자를 자동으로 생성합니다. 복사 / 이동 및 파괴도 마찬가지입니다. 사용자가 이러한 멤버 함수를 제공하지 않았으므로 C ++ 11 사양에서는이를 “사소한”클래스로 간주합니다. 따라서 memcpy처럼 내용을 초기화하는 등의 작업을 수행하는 것이 합법적입니다.
이:
struct NotTrivial
{
int foo;
NotTrivial() {}
};
이름에서 알 수 있듯이 이것은 더 이상 사소한 것이 아닙니다. 사용자가 제공하는 기본 생성자가 있습니다. 비어 있는지는 중요하지 않습니다. C ++ 11의 규칙에 관한 한, 이것은 사소한 유형이 될 수 없습니다.
이:
struct Trivial2
{
int foo;
Trivial2() = default;
};
이름에서 알 수 있듯이 이것은 사소한 유형입니다. 왜? 컴파일러에게 기본 생성자를 자동으로 생성하도록 지시했기 때문입니다. 따라서 생성자는 “사용자 제공”이 아닙니다. 따라서이 유형에는 사용자가 제공 한 기본 생성자가 없으므로 유형이 간단합니다.
= default
구문은 같은 기능의 생성을 방지 멤버 함수를 추가 복사 생성자 / 지정, 같은 일을 주로있다. 그러나 컴파일러에서 특수 동작을 트리거하기 때문에 기본 생성자 / 소멸자에서도 유용합니다.
답변
둘 다 사소하지 않습니다.
베이스와 멤버의 noexcept 사양에 따라 둘 다 동일한 noexcept 사양을 갖습니다.
지금까지 내가 감지하는 유일한 차이점 Widget
은 액세스 할 수 없거나 삭제 된 소멸자를 가진 기본 또는 멤버가 포함되어있는 경우입니다 .
struct A
{
private:
~A();
};
class Widget {
A a_;
public:
#if 1
virtual ~Widget() = default;
#else
virtual ~Widget() {}
#endif
};
그러면 =default
솔루션이 컴파일되지만 Widget
파괴 가능한 유형은 아닙니다. 즉,를 파괴하려고 Widget
하면 컴파일 타임 오류가 발생합니다. 그러나 그렇지 않은 경우 작업 프로그램이 있습니다.
Otoh, 사용자가 제공 한 소멸자를 제공 하면 다음을 파괴하는지 여부가 컴파일되지 않습니다 Widget
.
test.cpp:8:7: error: field of type 'A' has private destructor
A a_;
^
test.cpp:4:5: note: declared private here
~A();
^
1 error generated.
답변
사이의 중요한 차이점
class B {
public:
B(){}
int i;
int j;
};
과
class B {
public:
B() = default;
int i;
int j;
};
로 정의 된 기본 생성자 B() = default;
가 사용자 정의 아님으로 간주 됩니다 . 이는 다음 과 같이 값 초기화의 경우
B* pb = new B(); // use of () triggers value-initialization
생성자를 전혀 사용하지 않는 특별한 종류의 초기화가 발생하며 내장 유형의 경우 초기화 가 0 이됩니다 . 이 경우에는 발생 B(){}
하지 않습니다. C ++ 표준 n3337 § 8.5 / 7에 따르면
T 유형의 오브젝트를 값으로 초기화하는 것은 다음을 의미합니다.
— T가 사용자 제공 생성자
(12.1)를 가진 (가능하면 cv-qualified) 클래스 유형 (Clause 9 ) 인 경우, T의 기본 생성자가 호출됩니다 (T에 액세스 가능한 기본 생성자가없는 경우 초기화는 잘못 구성됩니다) );— T가 사용자 제공 생성자 가없는 (un-cv-qualified) 비 유니언 클래스 유형
인 경우, 객체는 0으로 초기화되며, 암시 적으로 선언 된 기본 생성자가 중요하지 않은 경우 해당 생성자가 호출됩니다.— T가 배열 유형 인 경우 각 요소는 값으로 초기화됩니다. — 그렇지 않으면 객체가 0으로 초기화됩니다.
예를 들면 다음과 같습니다.
#include <iostream>
class A {
public:
A(){}
int i;
int j;
};
class B {
public:
B() = default;
int i;
int j;
};
int main()
{
for( int i = 0; i < 100; ++i) {
A* pa = new A();
B* pb = new B();
std::cout << pa->i << "," << pa->j << std::endl;
std::cout << pb->i << "," << pb->j << std::endl;
delete pa;
delete pb;
}
return 0;
}
가능한 결과 :
0,0
0,0
145084416,0
0,0
145084432,0
0,0
145084416,0
//...