이 특정 경우에 멤버 이니셜 라이저 목록을 사용하는 것과 생성자에 값을 할당하는 것 사이에 차이가 있습니까?


90

내부적으로 그리고 생성 된 코드에 대해 실제로 다음과 같은 차이점이 있습니까?

MyClass::MyClass(): _capacity(15), _data(NULL), _len(0)
{
}

MyClass::MyClass()
{
  _capacity=15;
  _data=NULL;
  _len=0
}

감사...

답변:


61

이러한 값이 기본 유형이라고 가정하면 차이가 없습니다. 초기화 목록은 개체가 멤버로있는 경우에만 차이가 있습니다. 기본 초기화와 할당을 사용하는 대신 초기화 목록을 사용하면 개체를 최종 값으로 초기화 할 수 있습니다. 이것은 실제로 눈에 띄게 더 빠를 수 있습니다.


17
Richard가 말했듯이 이러한 값이 기본 유형이고 const이면 초기화 목록이 const 멤버에 값을 할당하는 유일한 방법입니다.
thbusch 2011 년

11
변수가 참조 또는 상수가 아닌 경우 설명 된 대로만 작동하며, 변수가 상수이거나 참조 인 경우 초기화 목록을 사용하지 않고 컴파일되지 않습니다.
stefanB 2011 년

77

상수 멤버, 참조 및 기본 클래스를 초기화하려면 초기화 목록을 사용해야합니다.

주석에서 언급했듯이 상수 멤버, 참조 및 매개 변수를 기본 클래스 생성자에 초기화해야하는 경우 초기화 목록을 사용해야합니다.

struct aa
{
    int i;
    const int ci;       // constant member

    aa() : i(0) {} // will fail, constant member not initialized
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0) { ci = 3;} // will fail, ci is constant
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0), ci(3) {} // works
};

예제 (완전하지 않은) 클래스 / 구조물에 참조가 포함되어 있습니다.

struct bb {};

struct aa
{
    bb& rb;
    aa(bb& b ) : rb(b) {}
};

// usage:

bb b;
aa a(b);

매개 변수가 필요한 기본 클래스를 초기화하는 예 (예 : 기본 생성자 없음) :

struct bb {};

struct dd
{
    char c;
    dd(char x) : c(x) {}
};

struct aa : dd
{
    bb& rb;
    aa(bb& b ) : dd('a'), rb(b) {}
};

4
그리고 만약 _capacity, _data_len접근 기본 생성자없이 클래스 유형이?
CB Bailey 2011 년

2
사용 가능한 생성자를 호출하고 더 많은 값을 설정해야하는 경우 생성자의 본문에서 호출합니다. 여기서 차이점 const은 생성자의 본문에서 멤버를 초기화 할 수없고 초기화 목록을 사용해야한다는 것입니다. const멤버가 아닌 멤버는 초기화 목록 또는 생성자 본문에서 초기화 할 수 있습니다.
stefanB 2011 년

@stefan : 생성자를 호출 할 수 없습니다. 기본 생성자가없는 클래스는 const 멤버와 마찬가지로 이니셜 라이저 목록에서 초기화해야합니다.
GManNickG 2011 년

2
또한 초기화 목록에서 참조를 초기화해야합니다
Andriy Tylychko 2011 년

2
@stefanB : 당신이 실제로 이해하지 못한다고 암시했다면 사과드립니다. 귀하의 답변에서 기본 또는 멤버가 initializer-list에서 이름이 지정되어야 할 때만 언급했으며 initializer-list에서 초기화하는 것과 생성자 본문에서 할당하는 것의 개념적 차이점이 실제로 내 오해 인 이유를 설명하지 않았습니다. 에서 왔을 수 있습니다.
CB Bailey

18

예. 첫 번째 경우 _capacity, _data_len상수로 선언 할 수 있습니다 .

class MyClass
{
private:
    const int _capacity;
    const void *_data;
    const int _len;
// ...
};

const예를 들어 런타임에 값을 계산하는 동안 이러한 인스턴스 변수의 -ness 를 보장하려는 경우 이는 중요합니다 .

MyClass::MyClass() :
    _capacity(someMethod()),
    _data(someOtherMethod()),
    _len(yetAnotherMethod())
{
}

const인스턴스 이니셜 라이저 목록에서 초기화 되거나 기본 유형이 매개 변수없는 공용 생성자를 제공해야합니다 (기본 유형이 수행함).


2
참조도 마찬가지입니다. 클래스에 참조 멤버가 있는 경우 이니셜 라이저 목록에서 초기화 해야 합니다.
Mark Ransom 2011 년

7

이 링크 http://www.cplusplus.com/forum/articles/17820/ 은 특히 ​​C ++를 처음 접하는 사람들에게 훌륭한 설명을 제공 한다고 생각합니다 .

intialiser 목록이 더 효율적인 이유는 생성자 본문 내에서 초기화가 아닌 할당 만 발생하기 때문입니다. 따라서 내장되지 않은 유형을 다루는 경우 생성자의 본문이 입력되기 전에 해당 객체의 기본 생성자가 이미 호출되었습니다. 생성자 본문 내에서 해당 개체에 값을 할당합니다.

실제로 이것은 기본 생성자에 대한 호출과 복사 할당 연산자에 대한 호출입니다. 이니셜 라이저 목록을 사용하면 복사 생성자를 직접 호출 할 수 있으며 때로는 훨씬 더 빠를 수 있습니다 (이니셜 라이저 목록이 생성자 본문 앞에 있음을 기억하십시오).


5

사용할 수있는 기본 생성자가없는 클래스 유형의 멤버가있는 경우 초기화가 클래스를 생성하는 유일한 방법이라고 덧붙일 것입니다.


3

큰 차이점은 할당이 부모 클래스의 멤버를 초기화 할 수 있다는 것입니다. 이니셜 라이저는 현재 클래스 범위에서 선언 된 멤버에서만 작동합니다.


2

관련된 유형에 따라 다릅니다. 차이점은

std::string a;
a = "hai";

std::string a("hai");

두 번째 형식은 초기화 목록입니다. 즉, 형식에 생성자 인수가 필요하거나 생성자 인수가 더 효율적인 경우 차이가 있습니다.


1

실제 차이점은 gcc 컴파일러가 기계 코드를 생성하고 메모리를 배치하는 방법에 달려 있습니다. 설명:

  • (phase1) init 본문 앞 (init 목록 포함) : 컴파일러는 클래스에 필요한 메모리를 할당합니다. 수업은 이미 살아 있습니다!
  • (phase2) init 본문에서 : 메모리가 할당되었으므로 이제 모든 할당은 이미 종료 / '초기화 된'변수에 대한 작업을 나타냅니다.

const 유형 멤버를 처리하는 다른 방법이 확실히 있습니다. 그러나 삶을 편하게하기 위해 gcc 컴파일러 작성자는 몇 가지 규칙을 설정하기로 결정했습니다.

  1. const 유형 멤버는 init 본문보다 먼저 초기화되어야합니다.
  2. 1 단계 이후 모든 쓰기 작업은 상수가 아닌 멤버에 대해서만 유효합니다.

1

기본 클래스 인스턴스와 비 정적 멤버 변수 를 초기화하는 한 가지 방법은 이니셜 라이저 목록을 사용하는 것입니다.

생성자의 이니셜 라이저 목록에 기본 또는 비 정적 멤버 변수를 지정하지 않으면 해당 멤버 또는 기본이 기본값으로 초기화됩니다 (멤버 /베이스가 비 POD 클래스 유형이거나 비 POD 클래스의 배열 인 경우). 유형) 또는 초기화되지 않은 상태로 둡니다.

생성자 본문이 입력되면 모든 기본 또는 멤버가 초기화되거나 초기화되지 않은 상태로 남게됩니다 (즉, 불확실한 값을 갖게됩니다). 생성자 본문에는 초기화 방법에 영향을 줄 기회가 없습니다.

생성자 본문의 멤버에 새 값을 할당 할 수 있지만 할당 할 수 없게 된 const클래스 유형의 멤버 나 멤버에는 할당 할 수 없으며 참조를 리 바인드 할 수 없습니다.

기본 제공 형식 및 일부 사용자 정의 형식의 경우 생성자 본문에 할당하면 이니셜 라이저 목록에서 동일한 값으로 초기화하는 것과 정확히 동일한 효과가있을 수 있습니다.

이니셜 라이저 목록에서 멤버 또는베이스의 이름을 지정하지 못하고 해당 엔티티가 참조이고 액세스 가능한 사용자 선언 기본 생성자가없는 클래스 유형이 있고 const규정되고 POD 유형이 있거나 POD 클래스 유형 또는 POD 클래스 유형의 배열 인 경우 함유 const규정하는 부재 (직접 또는 간접적으로) 그 프로그램이 잘못 형성된다.


0

이니셜 라이저 목록을 작성하면 모든 작업을 한 단계로 수행합니다. 이니 틸 라이저 목록을 작성하지 않으면 선언과 값 서명의 두 단계를 수행합니다.


0

생성자에서 초기화 목록과 초기화 문에는 차이가 있습니다. 아래 코드를 살펴 보겠습니다.

#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>

class MyBase {
public:
    MyBase() {
        std::cout << __FUNCTION__ << std::endl;
    }
};

class MyClass : public MyBase {
public:
    MyClass::MyClass() : _capacity( 15 ), _data( NULL ), _len( 0 ) {
        std::cout << __FUNCTION__ << std::endl;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

class MyClass2 : public MyBase {
public:
    MyClass2::MyClass2() {
        std::cout << __FUNCTION__ << std::endl;
        _capacity = 15;
        _data = NULL;
        _len = 0;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

int main() {
    MyClass c;
    MyClass2 d;

    return 0;
}

MyClass를 사용하면 생성자의 첫 번째 문이 실행되기 전에 모든 멤버가 초기화됩니다.

그러나 MyClass2를 사용하면 생성자의 첫 번째 문이 실행될 때 모든 멤버가 초기화되지 않습니다.

나중에 특정 멤버가 초기화되기 전에 누군가 생성자에 일부 코드를 추가하면 회귀 문제가 발생할 수 있습니다.


0

다른 사람들이 언급하지 않은 점은 다음과 같습니다.

class temp{
public:
   temp(int var);
};

임시 클래스에는 기본 ctor가 없습니다. 다음과 같이 다른 클래스에서 사용할 때 :

class mainClass{
public:
 mainClass(){}
private:
  int a;
  temp obj;
};

코드가 컴파일되지 않고 컴파일러가 초기화하는 방법을 모르기 obj때문에 int 값을받는 명시 적 ctor가 있으므로 다음과 같이 ctor를 변경해야합니다.

mainClass(int sth):obj(sth){}

따라서 그것은 단지 const와 reference에 관한 것이 아닙니다!

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