C++ 생성자(Constructor)와 초기화 리스트(Initialization List)

sjongyuuu·2021년 7월 20일
2

C++

목록 보기
5/6
post-thumbnail

이번 포스팅은 C++ 생성자와 초기화 리스트에 대해 다루어 보려고 한다.

생성자(Constructor) 개념을 먼저 짚고 넘어가도록 하자.

  • 생성자(Constructor)
    객체가 생성될 때 필드나 여러 절차들을 초기화하는 함수

정의를 보면 뭔가 대단한 말을 한 것 같지만,
그냥 생성자를 통해 초기화를 한다는 것에 초점을 두면 된다.😁

생성자의 특징은 다음과 같다.

  • 접근 제어자가 반드시 public으로 선언되어야 한다.
  • return 타입이 없다. 즉, 반환값이 없다.
  • 오버로딩(Overloading)을 통해 중복정의가 가능하다.

클래스 이름과 같은 이름으로 작성해야 한다는 것은 너무 당연하기 때문에 굳이 강조하지 않았다.

다음으로, 디폴트 생성자(Default Constructor)에 대해 알아보자.

  • 디폴트 생성자(Default Constructor)
    매개변수가 없거나 초기화된 매개변수(default parameter)를 가진 생성자

주요 특징이 클래스 내에 생성자가 없다면 컴파일러가 자동으로 디폴트 생성자를 만든다는 것이다.

class Test {
    int score;
public:
    Test() {} // default constructor
}

위 코드의 함수 Test와 같이 구현되지 않은 생성자가 컴파일러에 의해 자동으로 생성된다.

주의할 점으로
클래스 내에 생성자를 하나라도 작성했다면 컴파일러는 자동으로 디폴트 생성자를 만들지 않는다는 것이다.

마지막으로 생성자 호출 방법에 대해 알아보자.

class Test {
public:
    Test() {};
    Test(int n) {};
}

위 코드와 같이 Test 클래스가 정의된 경우 생성자 호출은 다음과 같이 할 수 있다.

Test t1;  // Default Constructor
Test t2(10);  // Constructor
Test t3 = Test(20);  // Constructor

t1과 같이 Test 클래스의 객체를 생성한 경우,
디폴트 생성자를 호출하여 초기화를 진행하게 된다.

✋ 여기서, 잠깐

주의할 점으로 디폴트 생성자를 호출하기 위해 다음과 같이 작성하면 안된다는 것이다.

Test t4();

이건 그냥, 이름이 t4이고 return 타입이 Test인 함수 원형을 선언한 것이다.

또 다른 경우, 같은 클래스 내에 2개 이상의 디폴트 생성자를 작성하면 안된다.

위와 같이 Test 클래스 내에 2개의 디폴트 생성자를 작성하고
main 함수에서 디폴트 생성자를 호출하려 한다면 어느 것을 호출해야 할지 알 수 없기 때문에 에러가 발생한다.

t2의 경우,
Test 클래스 내에 매개변수를 가진 생성자를 호출하는 것이다.

t3의 경우,
"Test(20)"을 통해 임시 객체를 생성하고 이를 Test 타입의 변수 t3에 대입하므로써 생성자를 호출하는 것이다.
이것은 앞에 t2와 똑같은 방법이라고 보면 된다.

다음으로 초기화 리스트(Initialization List)에 대해 알아보자.

  • 초기화 리스트(Initialization List)
    생성자에서 필드를 간단하게 초기화하는 방법

다음 코드를 보자.

class Test {
    int score;
public:
    Test(int s = 10) {
        score = s;
    }
}

위 코드의 경우, 생성자를 통해 필드를 초기화하는 일반적인 방법이다. 이를 초기화 리스트를 이용하여 다음과 같이 나타낼 수 있다.

class Test {
    int score;
public:
    Test(int s = 10): score(s) {}
}

필드에 선언된 변수들의 이름을 그대로 사용하여 작성하는데
이때, 변수의 이름이 매개변수의 이름과 같아도 상관없다.

🤔 여기서, 초기화 리스트를 굳이 사용하는 이유는 뭘까?

그것은 바로 초기화 리스트를 사용해야만 하는 상황이 발생하기 때문이다.
그 상황은 다음과 같다.

  • 필드에 const로 선언된 변수(상수)를 초기화하는 경우
  • 필드에 선언된 레퍼런스 변수를 초기화하는 경우

즉, 필드에 선언과 동시에 초기화가 이루어져야 하는 경우 초기화 리스트가 필요하다.

💡 먼저, 상수를 초기화하는 경우를 보자.

class Test {
    const int standard;
public:
    Test(int s = 10) {
        standard = s; // compile error!
    }
}

상수는 값 변경이 불가능한 것은 모두가 알고 있다.
따라서, 위 코드의 경우 생성자를 통해 상수 필드(standard)의 값을 변경하려하기 때문에 컴파일 에러가 발생하는 것이다.

여기서, 이 문제를 초기화 리스트를 통해 해결할 수 있다.

class Test {
    const int standard;
public:
    Test(int s = 10): standard(s) {}
}

이처럼 초기화 리스트를 통해 상수 필드의 초기화가 가능하고 첫 번째 코드에서 발생했던 에러를 해결할 수 있다.
또한, Test 클래스의 객체를 생성할 때마다 생성자의 인자로 서로 다른 값들을 넘겨주어 객체마다 서로 다른 상수값을 가질 수 있다.

아마 여기서, 초기화 리스트를 굳이 사용하지 않고 처음부터 상수 필드의 값을 초기화하면 되지 않을까 생각할 수 있다.

class Test {
    const int standard = 10;
public:
    Test(int s = 10) {}
}

그렇다. 사실, 위 코드와 같이 상수 필드를 직접 초기화해도 문제가 없다!😅

그러나, 이것은 C++11부터 허용된 것으로 이전 버전에서는 필드에서 상수의 초기화가 불가능했기 때문에 초기화 리스트의 사용이 불가피했다.

💡 다음으로, 필드에 선언된 레퍼런스 변수를 초기화하는 경우를 보자.

class Test {
    int &score;
public:
    Test(int &s) {
        score = s; // compile error!
    }
}

위 코드와 같이 생성자에서 레퍼런스 변수 score를 변수 s의 alias(별명)로 초기화하려 하는 경우
레퍼런스 멤버에 대한 초기화가 선언과 동시에 이루어지지 않았다는 에러 메세지가 출력된다.
따라서, 이때 초기화 리스트를 사용한다.

class Test {
    int &score;
public:
    Test(int &s): score(s) {}
}

이처럼, 초기화 리스트를 통해 score를 s의 alias로 초기화할 수 있다.

여기서, 주의할 점으로는 생성자의 매개변수 또한 레퍼런스 변수이어야 한다는 것이다.

만약, 매개변수가 일반적인 변수(지역 변수)로 선언된 경우 다음과 같은 문제가 발생한다.

다음 코드를 보자.

class Test {
    int &score;
public:
    Test(int s): score(s) {}
}

int main() {
    int x = 10;
    Test t1(x);
}

위와 같이 main 함수 내에서 지역 변수 x를 통해 Test 클래스의 객체의 멤버 score를 alias로 초기화하는 경우를 보자.

현재 변수 x는 메모리 상에 STACK 영역에 10을 저장한다.
이때, 지역 변수를 x를 인자로 넘겨주어 생성자를 호출하게 되면
변수 s가 생성되고 x의 값이 메모리 상에 STACK 영역에 저장된다.(x와 다른 위치)

여기서, score 변수는 변수 s의 alias로 s와 같은 주소를 나타낸다.

다음 그림을 보면 쉽게 이해할 수 있다.

그러나, 여기서 문제는 생성자의 실행이 종료되면 s가 메모리에서 사라진다는 것이다.
s가 사라지면 score는 메모리에서 엉뚱한 곳을 참조하고 있는 상태가 된다.

이 경우 컴파일 시에는 에러없이 진행되지만 score 변수에 접근하는 경우 사용자가 설정한 값이 아닌 임의의 쓰레기값을 반환하게 된다.

따라서, 필드에 레퍼런스 변수가 선언된 경우 생성자의 매개변수를 레퍼런스로 선언하여 초기화를 진행하도록 해야 한다.

이 경우는 다음 그림과 같이 나타난다.

.

.

.

C++ 생성자와 초기화 리스트 [끝]

  • 읽어 주셔서 정말 감사드립니다.
  • 오타나 잘못된 정보를 댓글로 남겨주시면 정말 감사하겠습니다.
profile
개발 잘 하고 싶다

0개의 댓글