C ++ 클래스 멤버를 초기화하기 위해 언제 이니셜 라이저 목록을 사용해야합니까?

StackOverflow https://stackoverflow.com/questions/1632484

  •  06-07-2019
  •  | 
  •  

문제

내가 가지고 있다고 가정 해 봅시다 std::map< std::string, std::string > m_someMap 클래스 A의 개인 회원 변수로서

두 가지 질문 : (그리고 내가 묻는 유일한 이유는 내가 그런 코드를 발견했기 때문입니다)

  1. 이 라인의 목적은 무엇입니까?

    A::A() : m_someMap()
    

    이제 나는 이것이 소모라는 것을 알고 있지만, 당신은 그렇게해야합니까? 나는 혼란 스럽다.

  2. 기본값은 무엇입니까? std::map< std::string, std::string > m_someMap, 또한 C#은 int, double 등을 정의합니다. 항상 defualt 0으로 초기화되고 객체는 널 (적어도 대부분의 경우)이므로 C ++의 규칙은 무엇입니까 ?? Defualt에 의해 객체를 초기화하고 쓰레기에 대한 프리미티브로 초기화됩니까? 물론 인스턴스 변수에 대해 복용하고 있습니다.

편집하다:

또한 대부분의 사람들은 이것이 스타일의 선택이며 필요하지 않다고 지적했기 때문에 다음과 같습니다.

A :: A () : M_SOMEMAP (), M_SOMEINT (0), M_SOMEBOOL (FALSE)

도움이 되었습니까?

해결책

M_SOMEMAP

  1. 당신은 할 필요가 없습니다.
  2. 당신이 그것을 생략하면 얻는 것 : 빈 std::map< std::string, std::string >, 즉, 그 안에 요소가없는 해당지도의 유효한 인스턴스.

M_SOMEBOOL

  1. 초기화해야합니다 true 또는 false 알려진 가치를 갖기를 원한다면. 부울은 "일반 구식 데이터 유형"이며 생성자의 개념이 없습니다. 또한, C ++ 언어는 비유 적으로 이니셜 화 부울에 대한 기본값을 지정하지 않습니다.
  2. 당신이 그것을 생략하면 얻는 것 : 지정되지 않은 가치를 가진 부울 멤버. 당신은 이것을하지 말고 나중에 그 가치를 사용해야합니다. 이 때문에이 유형의 모든 값을 초기화하는 것은 강력하게 권장되는 정책입니다.

m_someint

  1. 알려진 값을 원한다면 정수 값으로 초기화해야합니다. 정수는 "일반 기존 데이터 유형"이며 생성자 개념이 없습니다. 또한, C ++ 언어는 비유 적으로 시작된 정수의 기본값을 지정하지 않습니다.
  2. 당신이 그것을 생략하면 얻는 것 : 지정되지 않은 값을 가진 int 멤버. 당신은 이것을하지 말고 나중에 그 가치를 사용해야합니다. 이 때문에이 유형의 모든 값을 초기화하는 것은 강력하게 권장되는 정책입니다.

다른 팁

실제로 할 필요가 없습니다.
기본 생성자가 자동으로 수행합니다.

그러나 명시 적으로 만들어서 일종의 문서로 작용합니다.

class X
{
    std::map<string,string>  data;
    Y                        somePropertyOfdata;

    X()
      :data()                    // Technically not needed
      ,somePropertyOfdata(data)  // But it documents that data is finished construction
    {}                           // before it is used here.
};

C ++의 규칙은 POD 데이터를 명시 적으로 초기화하지 않는 한 다른 클래스에는 기본 생성자가 자동으로 호출되는 경우 (프로그래머가 명시 적으로 수행하지 않더라도) 정의되지 않은 경우입니다.

그러나 그렇게 말합니다. 이걸 고려하세요:

template<typename T>
class Z
{
     T  data;   
     Z()
        :data()    // Technicall not need as default constructor will
                   // always be called for classes.
                   // But doing this will initialize POD data correctly
                   // if T is a basic POD type. 
     {}
};

여기서는 기본값 초기화로 데이터를 발전시킵니다.
기술적으로 POD는 생성자가 없으므로 T가 int라면 무엇이든 할 것으로 예상합니까? 그것이 명시 적으로 초기화 되었기 때문에 POD 유형의 경우 0으로 설정되었습니다.

편집 :

class A
{
    std::map<string,string>   m_someMap;
    int                       m_someint;
    bool                      m_somebool;
   public:
    A::A()
       : m_someMap()      // Class will always be initialised (so optional)
       , m_someint(0)     // without this POD will be undefined
       , m_somebool(false)// without this POD will be undefined
    {}
};

다른 사람들이 지적했듯이 : 그것은 필요하지 않지만 스타일의 문제입니다. 거꾸로 : 기본 생성자를 명시 적으로 사용하고 싶어하고 코드를 더욱 장점으로 만듭니다. 단점 : 둘 이상의 CTOR가있는 경우 모든 CTOR가 변경을 유지하는 데 어려움을 겪을 수 있으며 때로는 클래스 멤버를 추가하고 CTORS 이니셜 라이저 목록에 추가하여 일관성이없는 것처럼 보이도록 잊어 버립니다.

A::A() : m_someMap()

이 라인은이 경우 불필요합니다. 하지만, 일반적으로, 클래스 멤버를 초기화하는 유일한 방법입니다.

이와 같은 생성자가있는 경우 :

X() : y(z) {
 w = 42;
}

그런 다음 다음이 발생합니다 X 생성자는 다음과 같습니다.

  • 먼저 모든 구성원이 초기화됩니다 y, 우리는 명시 적으로 우리가 z 그 주장으로. 을 위한 w, 발생하는 일은 유형에 따라 다릅니다 w. 만약에 w 포드 유형 (즉, 기본적으로 C 호환 유형 : 상속, 생성자 또는 소멸자, 모든 멤버 및 모든 멤버도 POD 유형입니다). ~ 아니다 초기화. 초기 가치는 그 메모리 주소에서 쓰레기가 발견 된 것입니다. 만약에 w POD 유형이 아닌 유형이면 기본 생성자가 호출됩니다 (비 POD 유형은 언제나 건축시 초기화).
  • 두 멤버가 모두 건설되면 우리 그 다음에 과제 연산자에게 전화하여 42를 할당하십시오 w.

주목해야 할 중요한 것은 모든 생성자가 호출된다는 것입니다. ~ 전에 우리는 생성자의 본문에 들어갑니다. 우리가 몸에 들어가면 모든 회원이 이미 초기화되었습니다. 따라서 생성자 본문에는 두 가지 가능한 문제가 있습니다.

  • 만약 w 기본 생성자가없는 유형입니까? 그러면 이것은 컴파일하지 않습니다. 그럼 ~ 해야 하다 후 명시 적으로 초기화됩니다 :, 처럼 y 이다.
  • 이 호출 순서가 있다면 어떨까요? 둘 다 기본 생성자 및 할당 연산자가 불필요하게 느리십니까? 아마도 올바른 생성자를 호출하는 것이 훨씬 더 효율적 일 것입니다.

요컨대, 그 이후로 m_someMap POD 유형이 아닌 유형입니다. 우리는 엄격하게 말할 필요가 없습니다. : m_someMap(). 어쨌든 기본적으로 구성되었을 것입니다. 그러나 포드 유형이거나 기본 생성자보다 다른 생성자를 호출하고 싶다면이를 수행해야했을 것입니다.

무슨 일이 일어나고 있는지 명확하게하기 위해 (두 번째 질문과 관련하여)

std::map< std::string, std::string > m_someMap m_somemap이라는 스택 변수를 생성하고 기본 생성자가 호출됩니다. 모든 객체에 대한 C ++의 규칙은 다음과 같은 경우입니다.

T varName;

여기서 t는 유형 인 경우 바르 이름이 기본적으로 구성됩니다.

T* varName;

새로운 표준에서 NULL (또는 0) 또는 NULLPTR에 명시 적으로 할당해야합니다.

기본값 문제를 명확히하기 위해 :

C ++는 일부 유형의 암시 적 개념이 회의적이라는 개념을 가지고 있지 않습니다. 무언가가 포인터로 명시 적으로 선언되지 않는 한, 그것은 할 수 없습니다 널 가치를 취하십시오. 이것은 그것을 의미합니다 모든 클래스에는 생성자 매개 변수가 지정되지 않은 경우 초기 값을 구축하기위한 기본 생성자가 있습니다. 기본 생성자가 선언되지 않으면 컴파일러가 귀하를 위해 하나를 생성합니다. 또한 클래스에 분류 된 유형의 멤버가 포함될 때마다 해당 멤버는 객체 구성에서 자체 기본 생성자를 통해 암시 적으로 초기화됩니다. ~하지 않는 한 콜론 구문을 사용하여 다른 생성자를 명시 적으로 호출합니다.

모든 STL 컨테이너 유형의 기본 생성자가 빈 컨테이너를 구축 할 수 있습니다. 다른 클래스에는 기본 생성자가하는 일에 대한 다른 규칙이있을 수 있으므로 여전히 이와 같은 상황에서 호출되고 있음을 알고 싶어합니다. 그것이 이유입니다 A::A() : m_someMap() 라인은 실제로 컴파일러에게 이미 어쨌든하는 일을하도록 지시하는 것입니다.

C ++에서 객체를 만들 때 생성자는 다음 순서를 거칩니다.

  1. 전체 클래스 트리에서 모든 상위 가상 클래스의 생성자에게 전화하십시오 (임의 순서로)
  2. 선언 순서대로 직접 상속 된 부모 클래스의 생성자에게 전화하십시오.
  3. 선언 순서대로 모든 멤버 변수의 생성자에게 전화하십시오.

이보다 몇 가지 세부 사항이 있으며 일부 컴파일러는이 특정 순서에서 몇 가지를 강요 할 수 있지만 이것이 일반적인 아이디어입니다. 이러한 생성자 호출 각각에 대해 생성자 인수를 지정할 수 있습니다.이 경우 C ++는 지정된대로 생성자를 호출하거나 단독으로 남겨두면 C ++가 기본 생성자를 호출하려고합니다. 기본 생성자는 단순히 인수가없는 것입니다.

가상 상위 클래스, 비 빈약 한 상위 클래스 또는 멤버 변수에 기본 생성자가 없거나 기본값 이외의 것으로 생성 해야하는 경우 생성자 호출 목록에 추가해야합니다. C ++는 기본 생성자 호출을 가정하기 때문에 기본 생성자를 목록에 넣고 완전히 남겨 두는 것 사이에는 전혀 차이가 없습니다 (C ++는 (이 질문의 범위를 벗어난 특별한 상황에서는) 어떤 종류의 생성자). 클래스에 기본 생성자가없고 생성자 호출을 제공하지 않으면 컴파일러에 오류가 발생합니다.

내장 유형에 관해서는 float 또는 int, 기본 생성자는 전혀 아무것도하지 않으므로 변수는 메모리에 남겨진 기본값을 갖습니다. 내장 된 모든 유형에는 사본 생성자가 있으므로 변수의 생성자에 대한 유일한 인수로 초기 값을 전달하여 초기화 할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top