생성자에서 콜론(:)은 왜 쓰는 건가요?

♪C++/STL 2018. 8. 7. 05:28

Foo(int num): bar(num) 은 초기화 리스트라고 하고, 멤버 변수 bar를 num으로 초기화하는 역할을 합니다.

//초기화 리스트
Foo(int num): bar(num) {};

//함수에서 초기화
Foo(int num)
{
   bar = num;
}

그냥 생성자 함수{} 내에서 초기화하는 것과, 이렇게 초기화 리스트를 쓰 는것의 차이는

초기화 리스트에서 초기화를 하는 경우, 생성자가 호출될 때 객체의 생성과 초기화가 한 번에 이루어집니다.

생성자 함수 내{}에서 초기화를 하는 경우, 객체가 생성되어, default생성자로 초기화된 상태에서 다시 한 번 할당받게 하게 됩니다. 이 경우엔 default할당-유저할당의 2단계를 거치게 돼서 오버헤드가 생깁니다.

초기화 리스트를 써야만 하는 상황은 크게 다음과 같습니다

  • 클래스가 레퍼런스를 멤버로 가질 때
  • non static const멤버가 있을 때
  • default 생성자가 없을 때
  • base class를 초기화할 때
  • 생성자 파라미터의 이름이 데이터 멤버랑 같을 때(이 경우는 this를 써서 해결할 수도 있습니다)

예를 들면

class MyClass
{
    public:
        int &i; //레퍼런스 멤버. 초기화 리스트를 써야 함
        int b;
        //Non static const 멤버. 초기화 리스트를 써야 함
        const int k;  

    //생성자 파라미터의 이름이 데이터 멤버랑 같음. 초기화 리스트를 쓸수 있음(선택 가능)
    MyClass(int a, int b, int c):i(a),b(b),k(c)
    {
        /*
        초기화 리스트를 쓰고 싶지 않은 경우
        this->a = a
        같이 써야 함
        */
    }
};

class MyClass2:public MyClass
{
    public:
        int p;
        int q;
        //base class인 MyClass가 default생성자가 없기 때문에 무조건 초기화 리스트에서 초기화해줘야 함
        MyClass2(int x,int y,int z,int l,int m):MyClass(x,y,z),p(l),q(m)
        {
        }

};

int main()
{
    int x = 10;
    int y = 20;
    int z = 30;
    MyClass obj(x,y,z);

    int l = 40;
    int m = 50;
    MyClass2 obj2(x,y,z,l,m);

    return 0;
}


출처 :  http://hyem2.tistory.com/

'♪C++ > STL' 카테고리의 다른 글

const 위치에 따른 구문의 의미  (0) 2018.08.03

const 위치에 따른 구문의 의미

♪C++/STL 2018. 8. 3. 01:10

C/C++ 기본 구문

- const 위치에 따른 구문의 의미

- by Tapitolife


지식iN에 이런 질문이 올라 왔더군요.

예제보니까 parameter에

void foo(int* const* bar)

이렇게 있더라고요;;

이게 definition으로서 가능해요? 만일 가능하다면 도대체 이렇게 정의하는 목적이 뭔지;;
그리고 만일 이게 된다면 const* int* bar도 되나요?? 답변 부탁드리겠습니다 ㅠ

원문 출처: http://kin.naver.com/qna/detail.nhn?d1id=1&dirId=1040101&docId=124932419

 

본디 상수 특성을 지정하는 const키워드는 그 놓인 위치에 따라 약간의 미묘한 의미 차이를
갖는데요…… 물론 이 질문에 제가 답변을 올렸고 채택이 되었습니다. ㅎㅎ
그러고 보니 저도 가끔 이 const 키워드가 들어갔을 때 그 의미를 혼동하는 경우가 있는데요.
이번 지식iN 답변을 계기로 저도 한번 제대로 const 키워드가 들어간 구문의 정확한 의미를
정리해 보렵니다.

 

const는 여러 곳에 놓일 수 있습니다. 그 때문에 의미가 혼동되기도 쉽죠.

변수 선언 시: [const] 자료형 [const] [포인터] [const] 변수명;
함수 선언 시: [const] 자료형 [const] [포인터] 함수명(매개변수…) [const] { }

뭐… 이렇게만 놓고 보면 무슨 말인지 감이 안 잡히니까,
예를 들어가면서 이야기 해 보기로 하죠. const는 어느 곳에 붙든 결국 원칙은 이겁니다.

 

"const가 붙으면 값 변경이 안 된다."

 

그렇다면 값 변경이 안 되는 경우(어디서 값 변경이 안 되는가?)를 찾는 것이 관건입니다.
질문 원문에서 소개 된 3가지 경우를 예로 들어 보죠.
1번. const int ** a,
2번. int * const * a,
3번. int ** const a

이 3가지의 자료형이 있습니다.
const가 붙는 위치가 조금씩 다른데요, 여러분은 이 차이를 명확히 구분하실 수 있습니까?
(만일 그렇다면 진정한 C언어 고수이십니다. ㅎㅎ)

 

위의 3가지 형식을 괄호로 쳐 본다면 다음과 같이 묶을 수 있습니다.
1번. (const int) ** a,
2번. (int * const) * a,
3번. (int **) const a

 

1번은 const int 형에 대한 이중 포인터입니다. 즉 int형 상수에 대한 이중 포인터이죠.
쉽게 나타낸다면 이런 과정으로 만들어지는 타입입니다.
const int x = 10;

const int * y = &x; // &x의 자료형이 const int** 형이라 보면 됩니다.
여기서 알맹이 값인 a = 10이라는 값이 상수처리 되는 거죠.

달리 말하면,
a = const int ** 형 (값 변경 O)
*a = const int * 형 (값 변경 O)
**a = const int 형 (값 변경 X)

void main()
{
const int a = 10;
const int * b = &a;
const int ** c = &b; // 이게 const int ** 형입니다.

c = 0x00000000; // 대입시 에러 없음
*c = 0x00000000; // 대입시 에러 없음
**c = 5; // 여기서 에러.
}

 

2번은 int * 형이 갖는 주소 값을 상수로 정했습니다.
즉 이런 과정을 통해 만들어지는 형식인 거죠.
int x = 10;
int * const y = &x; // &x의 자료형이 int * const * 형이라 보면 됩니다.
여기서 알맹이 값인 a = 10은 그냥 막 수정 가능하고, a의 주소를 갖고 있는 b가 상수인 거죠.

달리 말하면,
a = int * const * 형 (값 변경 O)
*a = int * const 형 (값 변경 X)
**a = int 형 (값 변경 O)

void main()
{
int a = 10;
int * const b = &a; // 여기서 &b가 100% 완벽한 int * const * 형을 갖게 됩니다.
int * const * c = &b; // 이게 int * const * 형입니다.

c = 0x00000000; // 대입시 에러 없음
*c = 0x00000000; // 여기서 에러.
**c = 5; // 대입시 에러 없음
}

 

3번은 int ** 형 자체가 상수형으로 지정되었습니다.
즉 이런 과정을 통해 만들어지는 형식인 거죠
int x = 10;
int * y = &x;
int ** const z = &y;

달리 말하면
a = int ** const 형 (값 변경 X)
*a = int * 형 (값 변경 O)
**a = int 형 (값 변경 O)

 

지식 iN에도 설명을 올렸지만 여기서 다시 한번 표로 정리를 해 보면 아래와 같습니다.

선언된 형식

const int ** a

int * const * a

int ** const a

a의 형식

const int ** 형
(변경 O)

int * const * 형
(변경 O)

int ** const 형
(변경 X)

*a의 형식

const int * 형
(변경 O)

int * const 형
(변경 X)

int * 형
(변경 O)

**a의 형식

const int 형
(변경 X)

int 형
(변경 O)

int 형
(변경 O)

 

const와 애스터리스크가 연달아 나오는 경우라면, 뒤에서부터 애스터리스크를
지워 나가는 방식을 추천해드리고 싶네요. 지워 나가다가 const가 걸리는 곳이
상수 취급 되는 곳이니까요.

strcpy 같은 함수를 보면 두 번째 인수가 const char * 형으로 되어 있을 겁니다.
풀이해 보면, 그 문자열의 주소는 경우에 따라 가변적이지만
문자열이 갖는 자체의 내용만큼은 변함이 없다. 이런 뜻입니다.

그럼 한번 테스트 해 볼까요?
아래 나온 코드들 간단한 거니까 직접 실습 해 보세요.

소스 A1

void test(const char * str)
{
str = "이건 뭥미?";
// str의 주소가 가리키는 문자열이 매개변수로 넘어온 문자열이 아닌
// "이건 뭥미?"라는 전혀 새로운 문자열로 바뀌었습니다.
}

 

소스 A2

void test(const char * const str)
{
str = "이건 뭥미?";
// 문자열 자체 내용도 불변이고
// 이 문자열의 주소 또한 상수로 선언되었기 때문에 여기서 에러가 납니다.
}

 

소스 B1

void test(char * str)
{
str[0] = 'A';
}

 

소스 B2

void test(const char * str)
{
str[0] = 'A';
}

 

소스 A1는 컴파일부터 실행까지 잘 되지만, 소스 A2는 컴파일 오류가 납니다.
A2에서는 str의 주소 자체가 상수이기 때문에 이후 변경될 수 없어서 생기는 오류입니다.
B1과 B2도 같은 맥락입니다.

 

그럼 이제 다 되었는가? 아닙니다. 하나 더 있습니다.
지금까지는 변수 선언을 할 때만 const가 붙었지만, C++에서 멤버 함수를 선언할 때
아예 함수에다가 const를 붙일 수도 있습니다.

class Point
{
public:
void PointPrint() const; // 이렇게 말이죠.
private:
int x;
int y;
};

void Point::PointPrint() const
{
printf("(%d, %d)\n", this->x, this->y);
}

 

이 때 함수에 붙이는 const의 의미는 무엇이냐 하면…

 

이 함수는 멤버 변수가 갖는 값을 가져다 쓸 수는 있어도 변경은 할 수 없다.

 

이런 의미를 갖습니다.

 

이런 예제를 보여드리면 될까요?

class Point
{
public:
void PointPrint() const; // 이렇게 말이죠.
private:
int x;
int y;
};

void Point::PointPrint() const
{
this->x = 10; this->y = 5; // 여기서 에러가 날 것입니다.
printf("(%d, %d)\n", this->x, this->y);
}



출처: http://tapito.tistory.com/31

'♪C++ > STL' 카테고리의 다른 글

생성자에서 콜론(:)은 왜 쓰는 건가요?  (0) 2018.08.07