상수 멤버 함수
Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 104개 중 29번째.
이전 레슨에서 게터(getter) 메서드에 사용된 const 키워드를 보았습니다. 하지만 함수의 매개변수 목록 뒤에 const가 나타나면 정확히 무엇을 의미할까요? 상수 멤버 함수(const member function)는 객체의 어떠한 멤버 변수도 수정하지 않겠다고 약속합니다.
class Rectangle {
int width;
int height;
public:
Rectangle(int w, int h) : width(w), height(h) {}
int getArea() const { // const 멤버 함수
return width * height; // 멤버 읽기가 허용됨
}
void setWidth(int w) { // 비-const - 객체를 수정함
width = w;
}
};매개변수 목록 뒤의 const는 컴파일러에게 "이 함수는 객체의 상태를 변경하지 않습니다"라고 알립니다. 만약 const 함수 내부에서 멤버 변수를 수정하려고 시도하면, 컴파일러가 에러를 발생시킵니다.
이는 const 객체나 const 참조를 다룰 때 필수적이게 됩니다. const 객체는 오직 const 멤버 함수만 호출할 수 있습니다:
void printArea(const Rectangle& rect) {
std::cout << rect.getArea(); // 성공 - getArea()는 const입니다
// rect.setWidth(10); // 오류 - setWidth()는 const가 아닙니다
}객체를 수정하지 않는 함수를 const로 표시하는 것은 좋은 관행입니다. 이는 의도를 문서화하고, 함수가 const 객체와 함께 작동할 수 있게 하며, 컴파일러가 실수로 인한 수정을 포착하도록 돕습니다. 데이터를 읽기만 하는 모든 멤버 함수는 const로 표시되어야 합니다.
챌린지
쉬움const 멤버 함수의 사용 시기와 이유를 보여주는 온도 변환기를 만들어 보겠습니다. 데이터만 읽는 메서드(const여야 함)와 객체의 상태를 수정하는 메서드가 포함된 클래스를 생성하게 됩니다.
코드를 정리하기 위해 두 개의 파일을 생성합니다:
Temperature.h: 온도 값을 저장하고 이를 읽고 수정하는 다양한 방법을 제공하는Temperature클래스를 정의합니다. 클래스에는 다음이 포함되어야 합니다:- 온도를 저장할 private 멤버
celsius(double) - 초기 섭씨 온도를 인자로 받는 생성자
- 저장된 값을 반환하는
getCelsius()메서드 — 데이터를 읽기만 하므로 const여야 합니다. celsius * 9.0 / 5.0 + 32.0공식을 사용하여 화씨 온도를 계산하고 반환하는getFahrenheit()메서드 — 아무것도 수정하지 않으므로 역시 const여야 합니다.celsius + 273.15를 사용하여 켈빈 온도를 반환하는getKelvin()메서드 — 이 또한 const여야 합니다.- 저장된 온도를 업데이트하는
setCelsius(double value)메서드 — 객체를 수정하므로 const가 될 수 없습니다. - 현재 온도에 delta를 더하는
adjustBy(double delta)메서드 — 역시 non-const입니다.
- 온도를 저장할 private 멤버
main.cpp: 일반 객체와 const 객체 모두에서 const 멤버 함수가 어떻게 작동하는지 보여줍니다. 입력에서 초기 온도 값을 읽은 후 다음을 수행합니다:- 입력값으로
Temperature객체를 생성합니다. "Initial: <celsius>C = <fahrenheit>F = <kelvin>K"를 출력합니다.- 온도를
10.0도만큼 조정합니다. "After adjustment: <celsius>C"를 출력합니다.- const 참조를 인자로 받아
"Reading: <celsius>C, <fahrenheit>F"를 출력하는 헬퍼 함수void printReadings(const Temperature& temp)를 작성합니다. 이 함수는 temp에 대해 const 메서드만 호출할 수 있습니다. - 작성한 온도 객체를 사용하여
printReadings()를 호출합니다. - 온도를
0.0(어는점)으로 설정합니다. "Freezing point: <celsius>C = <fahrenheit>F"를 출력합니다.
- 입력값으로
<iomanip>의 std::fixed와 std::setprecision(1)을 사용하여 모든 온도 값을 소수점 첫째 자리까지 표시하세요.
여기서 핵심은 printReadings()가 const 참조를 전달받기 때문에 const로 표시된 메서드만 호출할 수 있다는 점입니다. 이것이 getter 메서드를 const로 올바르게 표시하는 것이 중요한 이유입니다. 이를 통해 객체를 수정할 수 없는 상황에서도 해당 메서드들을 사용할 수 있게 됩니다.
치트 시트
const 멤버 함수는 객체의 어떠한 멤버 변수도 수정하지 않겠다고 약속합니다. const 키워드는 함수의 매개변수 목록 뒤에 위치합니다:
class Rectangle {
int width;
int height;
public:
int getArea() const { // const 멤버 함수
return width * height; // 멤버를 읽는 것은 허용됩니다
}
void setWidth(int w) { // 비-const - 객체를 수정함
width = w;
}
};const 멤버 함수는 멤버 변수를 읽을 수만 있고, 수정할 수는 없습니다. 만약 const 함수 내부에서 멤버 변수를 수정하려고 하면 컴파일러가 오류를 발생시킵니다.
const 객체 또는 const 참조는 const 멤버 함수만 호출할 수 있습니다:
void printArea(const Rectangle& rect) {
std::cout << rect.getArea(); // OK - getArea()는 const입니다
// rect.setWidth(10); // 오류 - setWidth()는 const가 아닙니다
}객체를 수정하지 않는 함수는 const로 표시하세요. 이는 의도를 문서화하고, 함수가 const 객체와 함께 작동할 수 있게 하며, 컴파일러가 실수로 인한 수정을 포착하도록 도와줍니다. 데이터를 읽기만 하는 모든 멤버 함수는 const로 표시해야 합니다.
직접 해보기
#include <iostream>
#include <iomanip>
#include "Temperature.h"
using namespace std;
// TODO: 여기서 Temperature 클래스 메서드들을 구현하세요
// 생성자
Temperature::Temperature(double initialCelsius) {
// TODO: celsius를 초기화하세요
}
// TODO: getCelsius()를 const로 구현하세요
// TODO: getFahrenheit()를 const로 구현하세요
// TODO: getKelvin()를 const로 구현하세요
// TODO: setCelsius(double value)를 구현하세요
// TODO: adjustBy(double delta)를 구현하세요
// TODO: const Temperature&를 매개변수로 받는 헬퍼 함수 printReadings를 만드세요
// 그리고 "Reading: <celsius>C, <fahrenheit>F"를 출력하세요
// Note: 이 함수는 temp에 대해 const 메서드만 호출할 수 있습니다!
int main() {
double initialTemp;
cin >> initialTemp;
// 출력 형식 설정
cout << fixed << setprecision(1);
// TODO: 입력값으로 Temperature 객체를 생성하세요
// TODO: "Initial: <celsius>C = <fahrenheit>F = <kelvin>K"를 출력하세요
// TODO: 온도를 10.0도만큼 조정하세요
// TODO: "After adjustment: <celsius>C"를 출력하세요
// TODO: 생성한 온도 객체로 printReadings()를 호출하세요
// TODO: 온도를 0.0(어는점)으로 설정하세요
// TODO: "Freezing point: <celsius>C = <fahrenheit>F"를 출력하세요
return 0;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.