[C++] 참조자와 Const, 초기화 리스트
LastMod:
👨💻 개인 공부 기록용 블로그 입니다.
💡 틀린 내용이나 오타는 댓글, 메일로 제보해주시면 감사하겠습니다!! (__)
42 Cursus - Cpp Module을 진행하기 위해 정리했었던 C++98 기본 개념들로, C++11, C++14와 다른 내용이 있을 수 있습니다.
각 개념은 정리한 시간 순으로 배치되어 있으며, 한 포스트에 배치된 개념이 크게 관련이 없을 수 있습니다.
참조자 (Reference)
- 참조자 (Reference)는 포인터와 유사하면서도 다른, 타 변수나 상수를 가리키는 방법이다.
- 기존 변수에 대한 또 다른 이름을 제공한다고 보면 된다. (alias)
#include <iostream>
//레퍼런스는 함수의 인자로도 쓰일 수 있다.
int change_val(int &p) {
p = 3;
return 0;
}
int main() {
int a = 3;
int& another_a = a;
//컴파일 타임에 another_a 가 a로 대치됨
another_a = 5;
// 따라서 a가 5로 변경된다.
//아래 코드는 a를 다시 3으로 변경시킨다.
change_val(a);
//change_val 함수가 인자로 레퍼런스를 받기 때문에,
//a의 값을 복사해서 넘기는게 아니라 a를 그대로 가져가게 된다.
//정확히는 a를 넘길 때에, 함수 내에서 int& p = a 로 p가 정의된 후 넘어간다고 생각하면 된다.
return 0;
}
- 레퍼런스는 선언 시에 반드시 누구의 별명이 될 것인지 지정해야 하며, 한 번 지정하면 절대로 변경할 수 없다.
- 또한 레퍼런스는 포인터와 다르게, 메모리 상에 존재하지 않을 수 있다.
- 단순 alias의 기능을 하는 경우에는, 조금만 생각해보면 메모리 상에 존재할 이유가 없다. 컴파일 타임에 대치해주면 되기 때문이다.
- 위와 같이 함수의 인자로 레퍼런스를 받을 때에는, 그 인자 레퍼런스는 스택 메모리를 차지한다.
- 레퍼런스의 레퍼런스, 레퍼런스의 배열, 레퍼런스의 포인터는 존재할 수 없다.
- 레퍼런스를 리턴하는 함수는 존재할 수 있지만, 지역변수의 레퍼런스를 리턴하지 않도록 주의해야 한다.
- 금지된 동작은 아니지만, seg fault를 유발시키기 딱 좋다.
Const 키워드 와 초기화 리스트 (Initialization Lists)
일반적인 상수의 의미
const int a = 1;
a = 2; //compile error
- 말 그대로, 초기 선언 후 값을 변경할 수 없는 변수를 이야기 한다.
- 하지만 const 상수를 사용하는 것 보다, #define을 이용한 매크로 상수나 enum을 활용한 열거형 상수의 사용이 권장된다.
const 포인터 변수
int a = 1;
int b = 2;
const int *ptr1 = &a; //*ptr1 자체를 상수화 (int 의 상수화)
int * const ptr2 = &a; //ptr2를 상수화 (int * 의 상수화)
*ptr1 = 3; //compile error
a = 2; //OK
ptr2 = &b; //complie error
//합치면 아래와 같이 쓸 수 있다. (ptr3이 가리키는 대상과 그 값을 모두 상수화)
const int * const ptr3 = &b;
- 위와 같이 const의 위치에 따라, 상수화 시키는 대상이 다르다.
const 멤버 함수
- 일반적으로 함수에는 const 키워드를 붙일 수 없다.
- 그러나 클래스의 멤버 함수는 const 키워드를 가질 수 있다.
int fn(void) const;
- const 멤버 함수는 해당 함수 내에서는 멤버 변수를 상수화 시키는 의미로 사용된다.
- 즉, 자신이 호출하는 객체를 수정하지 않는 읽기 전용 함수라는 이야기이다.
- 따라서, 해당 멤버 함수 내의 지역변수는 정상적인 사용이 가능하지만, 멤버 변수는 변경시킬 수 없다.
초기화 리스트 (Initialization Lists) 와 const 멤버 변수
-
클래스의 멤버 변수를
초기화
하는 방식//일반적인 변수는 int a = 10; int b; b = 10; //이렇게, 선언 직후 초기화 와 선언 후 대입이 큰 차이가 없지만 //클래스에서는 아주 큰 차이를 불러온다. //Person 이라는 클래스가 있고 멤버로 나이를 가진다고 가정했을 때, Person p1 = Person(42); //'멤버를 초기화 하는 생성자' 호출 Person p2; //디폴트 생성자 호출 p2 = Person(42); //'멤버를 초기화 하는 생성자' 호출 -> 대입 연산자 호출 //무려 연산이 3단계로 늘어나는 효과를 가져온다!
- 생성자를 정의할 때, 콜론(:) 후 멤버들을 나열하여 소괄호로 초기화 할 수 있다.
Person() : _age(42), _name(”HJ”) { //생성자 정의 };
- 초기화 리스트를 반드시 사용해야 하는 경우도 존재한다.
- 멤버 변수로 레퍼런스 타입을 가질 때
- 멤버 변수로 const 타입을 가질 때
- 레버런스와 const 변수는 대입이 불가능 하기 때문에, 반드시 초기화 리스트를 사용하여야 한다.
Leave a comment