C ++ 클래스 멤버를 명시 적으로 초기화하지 않으면 어떻게 초기화됩니까?


158

나는 개인 멤버들과 클래스가 있다고 가정 ptr, name, pname, rname, crnameage. 직접 초기화하지 않으면 어떻게됩니까? 예를 들면 다음과 같습니다.

class Example {
    private:
        int *ptr;
        string name;
        string *pname;
        string &rname;
        const string &crname;
        int age;

    public:
        Example() {}
};

그리고 나서

int main() {
    Example ex;
}

ex에서 멤버는 어떻게 초기화됩니까? 포인터는 어떻게됩니까? 수행 stringint얻을 기본 생성자와 0-intialized string()int()? 참조 멤버는 어떻습니까? const 참조는 어떻습니까?

더 알아야 할 것이 있습니까?

누구든지 이러한 사례를 다루는 자습서를 알고 있습니까? 아마 일부 책에서? 대학 도서관에서 많은 C ++ 서적을 이용할 수 있습니다.

더 나은 (버그 프리) 프로그램을 작성할 수 있도록 배우고 싶습니다. 모든 의견이 도움이 될 것입니다!



마이크, 나는 그것을 설명하는 어떤 책의 장을 의미합니다. 전체 책이 아닙니다! :)
bodacydo 2016 년

그래도 프로그래밍하려는 언어로 전체 책을 읽는 것이 좋습니다. 그리고 당신이 이미 읽었고 이것을 설명하지 않았다면, 그것은 좋은 책이 아니 었습니다.
Tyler McHenry

2
Scott Meyers (인기있는 전 프로 C ++ 어드바이스 전문가)는 Effective C ++ 에서 "규칙은 복잡합니다. 너무 복잡해서 암기 할만한 가치가 없습니다 .... 모든 생성자가 개체의 모든 것을 초기화해야합니다." 따라서 그의 의견으로는 "버그 프리"코드를 작성하는 가장 쉬운 방법은 규칙을 외우려고하지 않고 ( 실제로 그는 책에 배치 하지 않음 ) 모든 것을 명시 적으로 초기화하는 것입니다. 그러나 자신의 코드에서이 방법을 사용하더라도 그렇지 않은 사람들이 작성한 프로젝트에서 작업 할 수 있으므로 규칙이 여전히 유용 할 수 있습니다.
Kyle Strand

2
@TylerMcHenry C ++에 관한 어떤 책이 "좋은"것으로 간주됩니까? 나는 C ++에 관한 몇 권의 책을 읽었지만 그중 어느 것도 이것에 대해 완전히 설명하지 않았습니다. 필자의 이전 의견에서 언급 한 것처럼 Scott Meyers 는 Effective C ++ 의 완전한 규칙 제공을 명시 적으로 거부 합니다 . 또한 Meyers의 Effective Modern C ++ , Dewhurst의 C ++ 공통 지식 및 Stroustrup의 C ++ 둘러보기를 읽었습니다 . 내 기억으로 는 그들 중 누구도 완전한 규칙을 설명 하지 못했습니다 . 분명히 나는 ​​표준을 읽을 수 있었지만 나는 그 "좋은 책"을 거의 고려하지 않았다! : D 그리고 Stroustrup이 아마도 C ++ Programming Language 에서 설명 할 것으로 기대합니다 .
카일 스트랜드

답변:


207

명시 적 초기화 대신 클래스의 멤버 초기화는 함수의 로컬 변수 초기화와 동일하게 작동합니다.

들어 객체 , 기본 생성자가 호출된다. 예를 들어 for std::string의 경우 기본 생성자가 빈 문자열로 설정합니다. 객체의 클래스에 기본 생성자가 없으면 명시 적으로 초기화하지 않으면 컴파일 오류가 발생합니다.

들어 기본 유형 (포인터의 int 등), 그들은되어 있지 초기화 - 그들은 임의의 쓰레기가 이전에 해당 메모리 위치에있을 일이 무엇 포함되어 있습니다.

대한 참조 (예를 들어 std::string&), 그것을이다 불법 를 초기화하지, 당신의 컴파일러는 불평과 같은 코드를 컴파일을 거부합니다. 참조는 항상 초기화해야합니다.

따라서 특정 경우에 명시 적으로 초기화되지 않은 경우 :

    int *ptr;  // Contains junk
    string name;  // Empty string
    string *pname;  // Contains junk
    string &rname;  // Compile error
    const string &crname;  // Compile error
    int age;  // Contains junk

4
+1. 엄격한 표준 정의에 따르면 다양한 유형의 스토리지 (저장 영역)와 함께 기본 유형의 인스턴스는 모두 객체로 간주 됩니다 .
stinky472

7
사용자들은 "개체의 클래스는 기본 생성자가없는 경우 명시 적으로 초기화하지 않는 경우, 그것은 컴파일 오류가 될 것" 잘못 ! 클래스에 기본 생성자가 없으면 비어 있는 기본 기본 생성자 가 제공 됩니다.
Wizard

19
@ 위즈는 문자 그대로 '객체에 기본 생성자가없는 경우'와 같이 생성되지 않은 것으로 생각합니다. 클래스가 기본 이외의 생성자를 명시 적으로 정의하는 경우입니다 (기본 ctor가 생성되지 않음). 우리가 너무 pedanatic하면, 우리는 아마 도움보다 더 혼란 스러울 것입니다.
stinky472

8
@ wiz-loz foo생성자 가 있다고 말하면 암시 적입니다. 그러나 그것은 실제로 의미론의 논쟁입니다.
Tyler McHenry

4
"기본 생성자"를 인수없이 호출 할 수있는 생성자로 해석합니다. 이것은 스스로 정의하거나 컴파일러가 내재적으로 생성 한 것입니다. 따라서 그것의 부족은 스스로 정의하거나 생성하지 않았 음을 의미합니다. 또는 그것이 내가 보는 방법입니다.
5ound

28

먼저 mem-initializer-list 가 무엇인지 설명하겠습니다 . MEM-이니셜 목록은 쉼표로 구분이다 MEM-이니셜 각 S, MEM-초기화는 다음 멤버 이름 (이어서 발현리스트 a로 하였다는 ). 표현 목록은 회원이 구성되어 방법이다. 예를 들어

static const char s_str[] = "bodacydo";
class Example
{
private:
    int *ptr;
    string name;
    string *pname;
    string &rname;
    const string &crname;
    int age;

public:
    Example()
        : name(s_str, s_str + 8), rname(name), crname(name), age(-4)
    {
    }
};

사용자 제공 인수없는 생성자 의 mem-initializer-listname(s_str, s_str + 8), rname(name), crname(name), age(-4)입니다. 이 MEM-초기화리스트 것을 의미 name부재에 의해 초기화 개의 입력 반복자 소요 생성자 의 부재에 대한 참조로 초기화 상기 부재에 CONST 참조로 초기화되고 , 상기 부재는 값으로 초기화된다 .std::stringrnamenamecrnamenameage-4

각 생성자는 자체 mem-initializer-list 를 가지고 있으며 멤버는 정해진 순서 (기본적으로 멤버가 클래스에서 선언 된 순서)로만 초기화 될 수 있습니다. 따라서, 구성원 Example캔은 순서대로 초기화 : ptr, name, pname, rname, crname,와 age.

멤버 의 mem-initializer 를 지정하지 않으면 C ++ 표준은 다음과 같이 말합니다.

엔티티가 클래스 유형 ...의 비 정적 데이터 멤버 인 경우 엔티티는 기본적으로 초기화됩니다 (8.5). ... 그렇지 않으면 엔터티가 초기화되지 않습니다.

여기서는 name클래스 유형의 비 정적 데이터 멤버이므로 mem-initializer-list에 초기화자가name 지정 되지 않은 경우 기본적으로 초기화됩니다 . 다른 모든 멤버 에는 클래스 유형이 없으므로 초기화되지 않습니다.Example

표준들이 초기화되지 않은 것을 말할 때,이 방법은 그들이 가질 수 있는 값입니다. 따라서 위의 코드는 초기화되지 않았기 때문에 pname무엇이든 될 수 있습니다.

참조가 항상 초기화되어야하는 규칙과 같은 다른 규칙을 따라야합니다. 참조를 초기화하지 않는 것은 컴파일러 오류입니다.


내부를 너무 많이 표시하지 않고 선언 (in .h)과 정의 (in .cpp) 를 엄격하게 분리하려는 경우 멤버를 초기화하는 가장 좋은 방법 입니다.
Matthieu

12

선언 할 때 데이터 멤버를 초기화 할 수도 있습니다.

class another_example{
public:
    another_example();
    ~another_example();
private:
    int m_iInteger=10;
    double m_dDouble=10.765;
};

나는이 양식을 거의 독점적으로 사용하지만, 일부 사람들은 그것을 최근에 소개 되었기 때문에 '잘못된 양식'으로 간주한다고 읽었지만 C ++ 11에서 생각합니다. 나에게 그것은 더 논리적입니다.

새로운 규칙의 또 다른 유용한 측면은 클래스 자체 인 데이터 멤버를 초기화하는 방법입니다. 예를 들어 CDynamicString, 문자열 처리를 캡슐화하는 클래스 라고 가정하십시오 . 초기 값을 지정할 수있는 생성자가 있습니다 CDynamicString(wchat_t* pstrInitialString). 이 클래스를 다른 클래스 내부의 데이터 멤버로 사용하는 것이 좋습니다.이 경우 우편 주소를 저장하는 Windows 레지스트리 값을 캡슐화하는 클래스입니다. 이를 작성하는 레지스트리 키 이름을 '하드 코드'하려면 중괄호를 사용하십시오.

class Registry_Entry{
public:
    Registry_Entry();
    ~Registry_Entry();
    Commit();//Writes data to registry.
    Retrieve();//Reads data from registry;
private:
    CDynamicString m_cKeyName{L"Postal Address"};
    CDynamicString m_cAddress;
};

실제 우편 주소를 보유하는 두 번째 문자열 클래스에는 이니셜 라이저가 없으므로 기본 생성자가 작성시 호출됩니다 (아마도 자동으로 빈 문자열로 설정).


9

예제 클래스가 스택에서 인스턴스화되면 초기화되지 않은 스칼라 멤버의 내용은 무작위이며 정의되지 않습니다.

글로벌 인스턴스의 경우 초기화되지 않은 스칼라 멤버는 0이됩니다.

클래스의 인스턴스 인 멤버의 경우 기본 생성자가 호출되므로 문자열 객체가 초기화됩니다.

  • int *ptr; // 초기화되지 않은 포인터 (또는 전역 인 경우 0)
  • string name; // 생성자 호출, 빈 문자열로 초기화
  • string *pname; // 초기화되지 않은 포인터 (또는 전역 인 경우 0)
  • string &rname; //이 초기화에 실패하면 컴파일 오류
  • const string &crname; //이 초기화에 실패하면 컴파일 오류
  • int age; // 스칼라 값, 초기화되지 않은 임의 (또는 전역 인 경우 0)

나는 실험했고 string name스택에서 클래스를 초기화 한 후 비어있는 것처럼 보입니다 . 당신은 당신의 대답에 대해 절대 확신합니까?
bodacydo

1
string은 기본적으로 빈 문자열을 제공하는 생성자를 가질 것입니다-대답을 명확히하겠습니다
Paul Dixon

@bodacydo : Paul은 정확하지만이 동작에 관심이 있다면 명시 적으로 아프지 않습니다. 이니셜 라이저 목록에 버립니다.
Stephen

설명하고 설명해 주셔서 감사합니다!
bodacydo

2
무작위가 아닙니다! 무작위는 너무 큰 단어입니다! 스칼라 멤버가 랜덤이면 다른 난수 생성기가 필요하지 않습니다. 메모리에있는 삭제되지 않은 파일과 같이 데이터를 "남은"데이터를 분석하는 프로그램을 상상해보십시오. 심지어 정의되지 않았습니다! 일반적으로 우리는 기계가 무엇을하는지 알지 못하기 때문에 정의하기가 어렵습니다. 당신이 방금 삭제 한 "무작위 데이터"가 당신 아버지의 유일한 이미지라면, 당신의 어머니는 당신이 무작위라고 말하면 불쾌감을 느낄 수도 있습니다.
slyy2048

5

초기화되지 않은 비 정적 멤버에는 임의의 데이터가 포함됩니다. 실제로, 그들은 단지 그들이 할당 된 메모리 위치의 값을 가질 것입니다.

물론 객체 매개 변수 (와 같은 string)는 객체의 생성자가 기본 초기화를 수행 할 수 있습니다.

귀하의 예에서 :

int *ptr; // will point to a random memory location
string name; // empty string (due to string's default costructor)
string *pname; // will point to a random memory location
string &rname; // it would't compile
const string &crname; // it would't compile
int age; // random value

2

생성자가있는 멤버는 초기화를위한 기본 생성자가 있습니다.

다른 유형의 내용에 의존 할 수 없습니다.


0

스택에 있으면 자체 생성자가없는 초기화되지 않은 멤버의 내용은 임의적이며 정의되지 않습니다. 그것이 세계적이라하더라도 그것들이 제로화되는 것에 의존하는 것은 나쁜 생각입니다. 스택에 있는지 여부에 관계없이 멤버에 자체 생성자가 있으면 초기화를 위해 호출됩니다.

따라서 string * pname이 있으면 포인터에 임의의 정크가 포함됩니다. 그러나 문자열 이름의 경우 문자열의 기본 생성자가 호출되어 빈 문자열을 제공합니다. 참조 유형 변수의 경우 확실하지 않지만 임의의 메모리 덩어리에 대한 참조 일 수 있습니다.


0

클래스 구성 방법에 따라 다릅니다.

이 질문에 대답하면 C ++ 언어 표준의 거대한 스위치 사례 문과 단순한 필사자가 직관하기 어려운 문을 이해하게됩니다.

얼마나 어려운 일인지 간단한 예로

main.cpp

#include <cassert>

int main() {
    struct C { int i; };

    // This syntax is called "default initialization"
    C a;
    // i undefined

    // This syntax is called "value initialization"
    C b{};
    assert(b.i == 0);
}

기본 초기화에서는 https://en.cppreference.com/w/cpp/language/default_initialization 에서 시작합니다. "기본 초기화의 영향은 다음과 같습니다."부분으로 이동하여 case 문을 시작합니다.

  • "T가 비 -POD 인 경우 ": 아니오 (POD의 정의 자체가 큰 스위치 문임)
  • "T가 배열 유형 인 경우": 아니오
  • "그렇지 않으면 아무것도 수행되지 않습니다": 따라서 정의되지 않은 값이 남습니다.

그런 다음 누군가가 값을 초기화하기로 결정한 경우 https://en.cppreference.com/w/cpp/language/value_initialization "값 초기화의 영향"으로 이동 하여 case 문을 시작하십시오.

  • "T가 기본 생성자가 없거나 사용자가 제공하거나 삭제 한 기본 생성자가없는 클래스 유형 인 경우": 그렇지 않습니다. 이제 20 분 동안 해당 용어를 인터넷 검색합니다.
    • 암시 적으로 정의 된 기본 생성자가 있습니다 (특히 다른 생성자가 정의되지 않았기 때문에)
    • 사용자가 제공하지 않습니다 (내재적으로 정의 됨)
    • 삭제되지 않습니다 ( = delete)
  • "T가 사용자가 제공하거나 삭제하지 않은 기본 생성자를 가진 클래스 유형 인 경우": 예
    • "객체가 0으로 초기화 된 후 사소한 기본 생성자가있는 경우 기본으로 초기화됩니다."사소한 생성자가없고 단지 0으로 초기화됩니다. "zero-initialize"의 정의는 최소한 간단하고 기대하는 바를 수행합니다. https://en.cppreference.com/w/cpp/language/zero_initialization

이것이 바로 "암시 적"제로 초기화에 의존하지 않는 것이 좋습니다. 강력한 성능상의 이유가 없다면 생성자를 정의한 경우 또는 집계 초기화를 사용하여 모든 것을 명시 적으로 초기화하십시오. 그렇지 않으면 미래 개발자에게 매우 위험합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.