본문 바로가기

문과 코린이의, [C. C++] 기록/C++ 이론

[문과 코린이의 IT 기록장] C,C++ - 상수 (상수와 '함수, 포인터 변수, 클래스, &, 오버로딩'에 관한 내용 이해)

반응형

 

 

 

[문과 코린이의 IT 기록장] C,C++ - 상수 (상수와 '함수, 포인터 변수, 클래스, &, 오버로딩'에 관한 내용 이해)


 1. 상수란?
: 처음 정의시 그 값이 바로 주어지고, 한 번 저장된 값은 영원히 바뀌지 않는 것을 의미한다.
: 즉, 데이터 초기화가 이루어지면, 그 값을 바꿀 수 없도록 해 주는 것이다.
* 따라서 const변수 데이터 영역에 들어가게 되고, 컴파일과 동시에 데이터 영역 메모리로 올라가 버린다.

 1) 상수의 정의 방법
: const (상수의 형) (상수 이름) = (상수의 값) ;


 2) 상수 예시

 

 

 

 

Const int a = 3; // int형 상수 a는, 3의 값을 지닌다. 이 a의 값은 절대 변경될 수 없다.



 2. 상수 정의 시 주의사항
 1) 상수는 선언과 동시에 초기화되어야 한다.

 

 

 

 

- 상수는 처음에 한 번 저장된 값은 절대로 변하지 않는 특징을 가진다.
- 그렇기 때문에 처음 상수의 값과 관련해서 실행될 때, 정의해주지 않는다면 오류가 발생하게 된다.


 2) 상수의 값은 바꿀 수 없다.

 

 

 

 

- 상수는 어떠한 경우에도 값을 변경할 수 없는 불멸의 데이터이다. 따라서 이 코드는 오류가 발생한다.



 3. 상수와 함수
 1) const 사용 함수

ex )

 

 

 

 

int ShowPrint(int *num) const { ... }
- 이 함수const로 상수화 되어 있다.
- 즉 이 함수를 통해 값이 이루어지는 것을 방지하겠다는 뜻이다.
- 또한 이 함수는 const로 선언된 변수들 만이 사용될 수 있다는 뜻이다.



- cout이 사용될 수 있는 이유는 , cout이 ostream 클래스의 객체이고, 이 클래스의 cout 함수들은 많은 함수들을 오버로딩하고 있는 형태로 되어 있기 때문이다. (그 중에는 const로 선언된 함수도 포함되어 있다.)

2) const함수 초기화 방법
- const함수는
1. 선언과 동시에 초기화 해주거나,
2. 맴버 이니셜라이저를 통해 초기화가 가능하다.

ex )

 

 

 

 


3) 함수 const 매개변수

[ 파라미터(매개변수)의 종류 ]

 (1) In-Parameter

#include <iostream>

using namespace std;

 

void Output(int num){

    cout<<num<<endl;

}

int main(){

   int nNum = 10;

   Output(nNum);

   return 0;

}

- In-Parameter은 함수에 nNum이라는 데이터를 전달하는 것을 말한다.

- 즉, 값전달, 주소전달, 레퍼런스 전달이 모두 가능하다.

 

 

 (2) Out-Parameter

#include <iostream> 
using namespace std; 

void Output(int *num) { 
    *num = 5; 
} 

int main() { 
    int nNum = 10;
    cout << nNum << endl; 
    Output(&nNum); 
    cout << nNum << endl; 
    return 0;
}

- Out-Parameter은 함수에 의해 nNum이라는 데이터 값이 변경되는 것을 말한다.

- 즉, 주소전달, 레퍼런스전달만 가능하다.

 

[ 매개변수에서의 const ]

In-Parameter포인터 혹은 레퍼런스를 사용하고 싶은데, 값 변경의 위험이 느껴질 때, 매개변수 앞에 const를 붙여 상수화시켜준다.

ex 1 )

 

 

- 함수 내에서 num 값의 변경을 시도하려고 할 때, 에러가 발생되어서 값 변경을 불가능하게 한다.

cf ) 이는 맴버 변수를 변경하지 못하게 만드는, 클래스에서만 사용되는 (ex. void output(int &num) const)와는 다른 것이다.

 

ex 2)

- 클래스 형식이 In-Parameter로 전달될 때, 복사 생성자가 호출되는 오버헤드를 줄이기 위해 &를 붙이게 된다.

- 이 상황에서, 전달받는 객체의 값을 변경하지 못하게 하기 위해 const를 붙인다.

 

 

- 즉, In-Parameter로 쓰이는 포인터와 레퍼런스는 꼭 const를 쓸 필요가 있다.

 





 4. 포인터 변수와 const
- 포인터 변수에는 자료형 앞 혹은 뒤에 붙여서 사용하는, 두 가지 방법이 있다.

 

 

1) const int *p1 = &a;
p1 = &b; // 컴파일 가능
*p1 = 10; // 컴파일 오류 발생
- 가르키는 '주소의 값'을 변경하지 못한다.
- 즉, 대상체만 변경할 수 있으며, 대상체의 값을 변경하지는 못한다.

2) int * const p2 = &b;
p2 = &a; // 컴파일 오류 발생
*p2 = 10; // 컴파일 가능
- 가르키는 '주소(주소값)'를 변경할 수 없다.
- 즉, 대상체를 변경할 수 없으며, 대상체의 값만 변경할 수 있다.

 


[ 요약 ]

 

 

 

 


 


 5. 상수와 클래스
ex )

 

 

- 이 코드에서, 상수 객체의 맴버 변수값을 바꾸는 행위는, 모두 const 위반을 하므로 컴파일 에러를 발생시킨다.


1) 상수 객체에 대한 이해
Const A a;
// 생성자를 통해 상수 객체를 생성
// 따라서 이 객체는, '상수 맴버 함수'만 명시적으로 호출할 수 있다.


2) 상수 맴버 함수에 대한 이해
- 상수 맴버 함수란? // 이 구문은 클래스에서만 쓰인다.
: 호출한 객체의 맴버 변수를 변경할 수 없는 맴버 함수를 말한다. 상수 맴버 함수는 함수 선언에서 매개변수 목록 뒤에 const를 붙여준다.
ex) int SetNum() const { ... }

- 상수 맴버 함수의 특징
1. 상수 맴버 함수에서, 맴버 변수 값을 바꾸는 행위를 할 수 없다.
2. 상수 맴버 함수 안에서 비-상수 맴버 함수를 호출할 수 없다.
3. 상수 클래스 객체에 대해서도 호출할 수 있다.
ex ) int SetNum() const {
num = 3; // 컴파일 에러 1. 상수 맴버 함수에서, 맴버 변수 값을 바꾸는 행위를 할 수 없다.
GetNum(3); // 컴파일 에러 2. 상수 맴버 함수 안에서, 비-상수 맴버 함수를 호출할 수 없다.
return num;
}


3) 클래스 생성자에서의 상수
- C++ 클래스의 생성자는 맴버 변수를 초기화할 수 있어야 하므로, const 키워드의 사용을 허용하지 않는다.


4) const를 이용한 함수의 활용 예제 
ex )

 

 

(1) int sum () const{ // 상수 맴버 함수
// const로 선언된 sum함수.
// const 변수는 내부에서 사용할 수 있지만, 값에 대한 변경은 안된다.
cout<< "return n1 + n2" <<endl;
return n1 + n2;
// 따라서 n1 + n2를 반환해 주는 것이 가능하다. (n1, n2 모두 const변수이기 때문)
}


(2) const int sum(int num1, int num2){ // 반환형에 사용되는 const(상수)
// 반환값이 const로 선언되어 있다. 
// 따라서 반환값으로 전달되어 값을 저장할 때반드시 const 변수로 받아야 한다.
cout<< "return num1 + num2" <<endl;
return num1 + num2; // 이를 저장할 변수는, cosnt 변수여야 한다.
}


 


 6. 상수 참조 (const reference)
- 참조로 전달(Pass by reference)의 장점
: 값으로 전달의 경우 복사본이 만들어지게 되는데, 복사본이 필요하지 않은 경우가 대부분이므로, 참조로 전달을 사용해 불필요한 복사를 하지 않도록 도울 수 있음.

 

- const reference의 장점
: 프로그래머가 함수 안에서 전달된 인수를 변경할 가능성을 막기 위해, 상수로 참조를 만들면 효과적임

- 함수의 인자클래스의 객체를 전달할 수 있으며, 전달할 때 상수 참조를 이용하는 것이 일반적이다.

ex )

 

 

- PrintDate()함수 내에서의, date객체(인자로 받는 객체의 별칭)는 상수 객체로 취급되게 된다.

- 이 date객체가 GetYear(), GetMonth(), GetDay() 맴버 함수를 호출하게 되는데, 이 맴버함수들은 모두 상수 함수여야 한다.
- 즉, 상수 객체에 대해 비-상수 맴버 함수를 호출하면 컴파일 오류가 발생하므로, 상수 객체에 대해서는 상수 맴버 함수를 호출해야 한다.

 




 7. 상수 맴버 함수의 오버로딩 vs 비-상수 맴버 함수의 오버로딩
- 같은 맴버 함수에 대해서는 상수 버전과, 비-상수 버전으로 나누어 오버로딩이 가능하다.

 

 

 

 

- 맴버 함수의 상수 버전은 상수 객체에서 호출되며, 비-상수 버전은 비-상수 객체에서 호출된다.




 8. 리터럴  vs 상수

  상수 리터럴
공통점 변하지 않는 값 (데이터)
차이점
cf) 변하지 않는 변수를 뜻하는 것이기 때문에, 숫자가 올 수 있을뿐만 아니라, 객체, 유도형과 같은 데이터 등을 넣을 수 있다.


인스턴스(클래스 데이터)는 리터럴이 될 수 없다. 보통 인스턴스는 동적으로 사용하기 위해 작성되므로, 값이 언제 바뀔지 모른다.

 


* 유의사항
- 아직 공부하고 있는 문과생 코린이가, 정리해서 남겨놓은 정리 및 필기노트입니다.
- 정확하지 않거나, 틀린 점이 있을 수 있으니, 유의해서 봐주시면 감사하겠습니다.
- 혹시 잘못된 점을 발견하셨다면, 댓글로 친절하게 남겨주시면 감사하겠습니다 :)

 

반응형