2009-05-11 3 views
19

저는 C++을 처음 접했을뿐입니다. 내 현재 문제를 요약 한 다음 예제를 살펴보십시오.C++ - 클래스 내부에서 객체 생성하기

class Foo 
{ 
    //stuff 
}; 

class Bar 
{ 
    Foo foo; 
}; 

그래서 Bar는 참조 나 포인터뿐만 아니라 전체 Foo 객체를 유지합니다. 이 객체는 디폴트의 생성자으로 초기화되고 있습니까? 명시 적으로 생성자를 호출해야합니까? 그렇다면 어떻게 그리고 어디서?

감사합니다.

+4

나는 C++에 익숙해 있으며, 나는 아직도이 정도까지 각각의 의심을 가지고있다. 더욱이 대부분의 대답은 Foo 기본 ​​생성자가 호출 될 것이고 그 사실은 Foo 자체의 정의에 달려 있다는 사실을 분명하게 설명합니다. 사용자 제공 또는 암시 적 기본 생성자입니까? 그것은 어떤 개인 회원 속성을 가지고 있습니까? C++의 초기화는 간단하지 않습니다. –

+2

@xtofl이 포스터에 'C++에 익숙합니다'라는 메시지를 보내는 것은 대단히 재미 있습니다 ... 거의 대부분의 사람들이 거의 모든 대답이 잘못되었을 때 C++에 익숙하지 않은 것 같습니다. 사실 초기 초기화는 어렵습니다. 응답 한 사람들 중 일부는 C++ 지식 @ JaredPar, @dirkgently, @ David Shornley를 입증했지만 아직 실패했습니다. –

답변

17

의 기본 생성자. 명시 적으로 바의 생성자 안에 foo는의 생성자를 호출하지 않으면

class Foo 
{ 
    public: 
    Foo(int val) { } 
    //stuff 
}; 

class Bar 
{ 
    public: 
    Bar() : foo(2) { } 

    Foo foo; 
}; 
+0

구문 오류 : public 키워드 다음에 콜론 (:)이 필요합니다. – dirkgently

+2

그리고 각 클래스의 닫기} 뒤에 세미 콜론 (;)이 있습니다. 나는 자바를 비난한다. 구문 수정에 대해 – richq

+0

주셔서 감사합니다. 나는 C#를 비난한다. –

1

So Bar constains a full Foo object, not just a reference or pointer. Is this object initialized by its default constructor?

Foo 당신이 유형 Bar의 객체를 만들 때 기본의 ctor를 사용하는 기본 ctor에 입력 Foo의 객체가있는 경우. 그렇지 않으면 Foo을 직접 호출해야합니다. 그렇지 않으면 Bar의 코드가 컴파일러를 불편하게 만듭니다.

예 : 다음과 같이 위의 예제를 수정

"In constructor 'Bar::Bar()': Line 5: error: no matching function for call to 'Foo::Foo()'

Do I need to explicitly call its constructor, and if so, how and where ?

:

class Foo { 
public: 
Foo(double x) {} 
}; 

class Bar { 
Foo x; 
}; 

int main() { 
Bar b; 
} 

위의 컴파일러 것 같은 것을 불평 그것은에 의해 초기화됩니다

class Foo { 
public: 
    Foo(double x) {} // non-trivial ctor 
}; 

class Bar {  
Foo x; 
public: 
    Bar() : x(42.0) {} // non-default ctor, so public access specifier required 
}; 

int main() { 
Bar b; 
} 
+0

Foo에 정의 된 생성자가 없으면 (아마도 복사 생성자 외에) 컴파일러는 전혀 불평하지 않지만 암시 적으로 정의 된 Foo 생성자를 호출하지 않습니다. 개체가 초기화되지 않습니다. –

+0

그것이 내가 기본적으로 의미하는 바입니다. – dirkgently

+0

초기화리스트를 사용하지 않고 객체를 초기화 할 수 있습니까? 마찬가지로 생성자의 {} 내부에서 객체를 초기화 할 수 있습니까? –

2

다음의 기본 중 하나가 사용됩니다 : 다른 생성자를 사용하려는 경우, 당신은 이런 식으로 뭔가가있을 수 있습니다. 명시 적으로 이것은 당신이 코드 :

+1

저는 여러분 대부분이 여러분의 예제에서 "42"를 생각해 냈습니다. 그게 어디서 온거야? – ichiban

+7

그것은 H2G2에 대한 참고서입니다 - 생명, 우주 및 모든 것에 대한 궁극적 인 질문에 대한 답변. 읽기 : http://en.wikipedia.org/wiki/42_(number)#In_The_Hitchhiker.27s_Guide_to_the_Galaxy. Douglas Adams를 더 잘 읽습니다. – dirkgently

+0

@ichiban, @dirkgently입니다. – JaredPar

1

전체 개체에 푸 : 푸 (int)를 추가 가정 물론이다 생성자에게

Bar::Bar() : foo(42) {} 

를 호출하여 제어 할 수 있습니다. 아니요, Bar의 기본 생성자에서 기본값으로 생성됩니다.

이제 푸 (Foo)가 int를 취한 생성자 만 있으면.

class Foo { 
public: 
    Foo(int x) { .... } 
}; 

class Bar { 
public: 
    Bar() : foo(42) {} 

    Foo foo; 
}; 

그러나 푸에 기본 생성자 푸()를 한 경우, 컴파일러는 자동으로 바의 생성자를 생성하고, 그 푸의 기본을 부를 것이다 : 당신은 푸의 생성자를 호출하고, 즉 무슨 말을 할 줄에 생성자가 필요할 것 (ie Foo())

+0

저는 여러분 대부분이 여러분의 예제에서 "42"를 생각해 냈습니다. 그게 어디서 온거야? – ichiban

+0

실용적이지 않습니다. 모든 ctor는 기본적으로 비공개입니다. – dirkgently

+0

결정된. 생각/다른 개념에 대해 이야기 할 때 나는 그것을 버려가는 경향이 있습니다. – Macke

1

특별히 지정하지 않는 한, foo는 기본 생성자를 사용하여 초기화됩니다. 당신이 다른 생성자를 사용하려는 경우, 당신은 막대의 초기화 목록에 그렇게해야합니다

Bar::Bar(int baz) : foo(baz) 
{ 
    // Rest of the code for Bar::Bar(int) goes here... 
} 
+0

Foo에 사용자 정의 된 기본 생성자가 있으면 호출됩니다. Foo에서 암시 적으로 정의 된 기본 생성자의 경우 Bar의 암시 적으로 정의 된 생성자에서 호출이 수행되지 않습니다. –

+0

초기화 목록을 사용하지 않고 객체를 초기화 할 수 있습니까? 마찬가지로 생성자의 {} 내부에서 객체를 초기화 할 수 있습니까? –

+0

@JustinLiang, 아니요. 일단 생성자의 본문이 실행되면 초기화 될 클래스의 일부인 객체가 초기화됩니다. 객체에 기본 생성자가 없으면 이니셜 라이저 목록에서 생성자를 호출해야합니다. 그러나 할당을 사용하여 클래스의 객체와 데이터를 변경할 수는 있지만 객체가 초기화되고 할당을 통해 덮어 쓰기 때문에 효율성이 떨어집니다. – Naaff

0

당신은 C++에서 명시 적으로 기본 생성자를 호출 할 필요가 없습니다, 당신을 위해 호출됩니다. 다른 생성자를 호출하고 싶다면, 당신이 할 수 있습니다 :

Foo foo(somearg) 
+0

Foo에 사용자 정의 된 기본 생성자가 있으면 호출됩니다. Foo에서 암시 적으로 정의 된 기본 생성자의 경우 Bar –

4

이 가능하면 C++ 컴파일러는, 각 클래스에 대해 생성됩니다 네 가지 기능이있다, 당신이 그들을 제공하지 않는 경우 : 기본 생성자 , 복사 생성자, 대입 연산자 및 소멸자가 포함됩니다.C++ 표준 (12 장, "특수 함수")에서 이러한 함수는 "암시 적으로 선언 된"및 "암시 적으로 정의 된"이라고합니다. 그들은 공개적으로 접근 할 것입니다.

생성자에서 "암시 적으로 정의 된"과 "기본값"을 혼동하지 마십시오. 기본 생성자는 인수없이 호출 할 수있는 생성자입니다 (있는 경우). 생성자를 제공하지 않으면 기본 생성자가 암시 적으로 정의됩니다. 각 기본 클래스 및 데이터 멤버에 대해 기본 생성자를 사용합니다.

그래서 Foo 클래스에는 암시 적으로 정의 된 기본 생성자가 있고 Bar (사용자 정의 생성자가없는 것)는 Foo의 기본 생성자를 호출하는 암시 적으로 정의 된 기본 생성자를 사용합니다.

Bar의 생성자를 작성하려는 경우 초기화 프로그램 목록에서 foo를 언급 할 수 있지만 기본 생성자를 사용하고 있으므로 실제로 지정하지 않아도됩니다.

Foo 용 생성자를 작성하면 컴파일러는 자동으로 기본 생성자를 생성하지 않으므로 필요한 경우 생성자를 지정해야합니다. 따라서 Foo(int n);과 같은 것을 Foo의 정의에 넣고 기본 생성자 (Foo(); 또는 Foo(int n = 0);)를 명시 적으로 작성하지 않은 경우 사용할 수 없으므로 현재 양식으로 바를 가질 수 없습니다 푸의 기본 생성자. 이 경우 bar 생성자가 Foo를 초기화하는 것과 같은 생성자가 있어야합니다. Bar(int n = 0): foo(n) {}. (Bar 생성자가 먼저 foo를 초기화하려고 시도 했으므로 Bar(int n = 0) {foo = n;} 등이 작동하지 않는다는 것에주의하십시오.

+0

의 암시 적으로 정의 된 생성자에서는 호출이 수행되지 않습니다. Bar의 암시 적으로 정의 된 생성자는 Foo에서 암시 적으로 정의 된 생성자를 호출하지 않습니다. Foo에 사용자 정의 생성자가있는 경우 Bar는 암시 적으로 생성자 will (이번에는 yes)을 정의하여 Foo에서 사용자 정의 생성자를 호출합니다. 그럼에도 불구하고 여기까지 가장 완벽한 대답은 +1 –

+0

입니다. 12.6.2 (4)에 따르면 비 정적 데이터 멤버는 이니셜 라이저 목록에 언급되지 않은 경우 기본 생성자로 초기화되고 8.5 (5)에서는 기본 생성자가 POD가 아닌 경우 호출됩니다 (일반 올드 데이터, 편리하게 정의되지 않음). 표준에서) 형식으로,이 경우 0으로 초기화됩니다. 그래서 Foo가 POD 유형인지 아닌지를 묻습니다. 우리는 보지 못합니다. POD 유형이라면 모두 0으로 초기화됩니다. 그것이 POD가 아닌 것으로 표시하는 것들 중 하나를 가지고 있다면 그것은 디폴트 초기화되고 따라서 암묵적으로 정의 된 디폴트 생성자를 호출합니다. –

12

C++에서는 상당히 어려운 주제입니다. 간단한 대답은 이며, 이는에 달려 있습니다. Foo가 초기화되었는지 여부는 Foo 자체의 정의에 따라 다릅니다. 두 번째 질문 : 바 초기화 방법 Foo : 초기화 목록이 답입니다.

일반적으로 Foo는 암시 적 기본 생성자 (컴파일러가 생성 됨)에 의해 기본값으로 초기화되지만 사실 일 필요는 없습니다.

Foo에 사용자 정의 기본 생성자가 없으면 Foo가 초기화되지 않습니다. 더 정확하게하려면 : 바 또는 푸 사용자 정의 기본 생성자를 부족의 각 구성원이 바의 컴파일러 생성 된 기본 생성자에 의해 초기화되지 않은 것 :

class Foo { 
    int x; 
public: 
    void dump() { std::cout << x << std::endl; } 
    void set() { x = 5; } 
}; 
class Bar { 
    Foo x; 
public: 
    void dump() { x.dump(); } 
    void set() { x.set(); } 
}; 
class Bar2 
{ 
    Foo x; 
public: 
    Bar2() : Foo() {} 
    void dump() { x.dump(); } 
    void set() { x.set(); } 
}; 
template <typename T> 
void test_internal() { 
    T x; 
    x.dump(); 
    x.set(); 
    x.dump(); 
} 
template <typename T> 
void test() { 
    test_internal<T>(); 
    test_internal<T>(); 
} 
int main() 
{ 
    test<Foo>(); // prints ??, 5, 5, 5, where ?? is a random number, possibly 0 
    test<Bar>(); // prints ??, 5, 5, 5 
    test<Bar2>(); // prints 0, 5, 0, 5 
} 

이제 푸 다음 사용자 정의 생성자가 있다면, 그것은 것 Bar가 사용자 초기화 된 생성자를 가지고 있는지 여부에 관계없이 항상 초기화됩니다. Bar가 Foo의 (암시 적으로 정의 된) 생성자를 명시 적으로 호출하는 사용자 정의 생성자를 가지고 있으면 Foo가 실제로 초기화됩니다. Bar의 초기화 목록이 Foo 생성자를 호출하지 않으면 Bar에 사용자 정의 생성자가없는 경우와 동일합니다.

테스트 코드에 대한 설명이 필요할 수 있습니다. 컴파일러가 실제로 생성자를 호출하는 사용자 코드없이 변수를 초기화하는지 여부에 관심이 있습니다. 우리는 객체가 초기화되었는지 여부를 테스트하려고합니다. 이제는 함수에 객체를 만들면 손대지 않았고 이미 0이 포함 된 메모리 위치에 도달 할 수 있습니다. 행운을 성공과 차별화하기 위해 함수에 변수를 정의하고 함수를 두 번 호출합니다. 첫 번째 실행에서는 메모리 내용을 인쇄하고 강제로 변경합니다. 함수에 대한 두 번째 호출에서 스택 추적이 동일하기 때문에 변수는 정확히 동일한 메모리 위치에 보관됩니다. 초기화 된 경우에는 0으로 설정되고, 그렇지 않으면 정확히 동일한 위치에있는 이전 변수와 동일한 값을 유지합니다.

각 테스트 실행에서 인쇄 된 첫 번째 값은 초기화 된 값 (실제로 초기화 된 경우) 또는 해당 메모리 위치의 값으로, 경우에 따라 0이됩니다. 두 번째 값은 단지 테스트입니다 수동으로 변경 한 후 메모리 위치의 값을 나타내는 토큰. 세 번째 값은 함수의 두 번째 실행에서 가져옵니다. 변수가 초기화되면 0으로 되돌아갑니다. 객체가 초기화되지 않으면 메모리가 이전 내용을 유지합니다.

+0

테스트 ()을 실행할 때 왜 출력이 ??, 5, ??, 5 대신 ??, 5, ??, 5가 아니라면 test_internal이 반환 할 때 Foo 객체 (x)가 범위를 벗어나지 않습니다. ? 테스트 ()과 유사하게? – user1084113

+0

@ user1084113 : 정의되지 않은 동작이므로 두 가지 중 하나를 얻을 수는 있지만 컴파일러/아키텍처에서 스택을 공통으로 사용하는 방법에 대한 지식이 남용됩니다. 기본적으로 함수가 입력되면 내부 변수에 대한 스택 조각을 가져옵니다.이 내부 변수는 함수 호출이 호출자 또는 스택 포인터를 업데이트하는 함수 일 수 있습니다. 함수에 대한 두 번째 호출은 동일한 순서로 놓인 지역 변수에 대해 동일한 메모리 블록을 사용합니다. 'x'는 초기화되지 않고 함수의 마지막 실행에 값이 할당됩니다. –

관련 문제