상수 멤버 함수 const
함수 선언 및 구현시 함수 괄호 ( ) 뒤에 const가 붙는 함수
- void print() const
- 상수 멤버 함수 안에서는 모든 멤버를 상수 취급한다.
- 멤버 데이터의 값을 읽을 수는 있지만 변경할 수는 없다.
- 코드 작성시 안정성
- 상수 객체는 상수 멤버 함수만 호출할 수 있다.
#include <iostream>
class Point
{
int x, y;
public:
Point(int a = 0, int b = 0) : x(a), y(b) {}
void set( int a, int b ) { x = a; y = b; }
void print() const // 상수 멤버 함수
{
x = 10; // error : 상수 멤버 함수 안에서 모든 멤버를 상수 취급하기 때문에 변경시 에러!
std::cout << x << ", " << y << std::endl;
}
};
int main()
{
}
main.cpp:13:13: error: assignment of member ‘Point::x’ in read-only object
const를 사용하지 않은 일반 class 와 일반 객체 pt 선언, 값 변경, 함수 호출 등의 코드로
아무 문제 없이 수행된다.
#include <iostream>
class Point
{
public:
int x, y;
Point(int a = 0, int b = 0) : x(a), y(b) {}
void set( int a, int b ) { x = a; y = b; }
void print()
{
std::cout << x << ", " << y << std::endl;
}
};
int main()
{
Point pt(1,1);
pt.x = 10;
pt.set(10,10);
pt.print();
}
상수 객체를 선언했을 때는 객체 내 변수의 값을 상수로 취급해 값을 변경할 수 없다.
또한 클래스 내에 정의된 상수 함수만 호출이 가능하다.
상수 함수로 선언된 print()는 정상 동작한다.
#include <iostream>
class Point
{
public:
int x, y;
Point(int a = 0, int b = 0) : x(a), y(b) {}
void set( int a, int b ) { x = a; y = b; }
void print() const // 상수 멤버 함수 : 나는 값을 안 바꿀 거야 라고 약속 (=const)
{
std::cout << x << ", " << y << std::endl;
}
};
int main()
{
const Point pt(1,1); // 상수 객체
pt.x = 10; // error
pt.set(10,10); // error
pt.print(); // print()를 상수 함수로 만들었기에 OK
}
main.cpp:21:12: error: assignment of member ‘Point::x’ in read-only object
main.cpp:22:17: error: passing ‘const Point’ as ‘this’ argument discards qualifiers [-fpermissive]
상수 멤버 함수 : 선언부, 구현부 예제
함수를 선언과 구현으로 분리할 때는 선언과 구현 모두 const를 표기해야 한다.
상수 멤버 함수 선언부, 구현부 모두 const를 붙여야 한다.
// =========== Point.h ============
class Point
{
public:
int x, y;
Point(int a = 0, int b = 0);
void set(int a, int b);
void print() const ; // 선언부에서 const 로 선언!
};
// =========== Point.cpp ============
#include "Point.h"
#include <iostream>
Point::Point(int a, int b)
: x(a), y(b)
{
}
void Point::set( int a, int b )
{
x = a; y = b;
}
void Point::print() const // 구현부에도 역시 const
{
std::cout << x << ", " << y << std::endl;
}
만약 위의 구현부(Point.cpp)에서
void Point::print() const 가 아닌
void Point::print() 로 구현을 하면 아래와 같은 에러가 발생한다.
Point.h:10:10: error: candidate is: void Point::print() const
상수 멤버 함수 with "call by reference"
foo 의 input 값이 r 인데 , 이 r 은 const로 정의한다. r.getArea()를 호출하려면 getArea()가 const로 정의되어 있어야 한다.
초보 개발자는 int getArea() 에 const를 붙이지 않고, 그냥 void foo( const Rect& r) 의 const를 지워 해결하는 경우가 많다.
const가 들어가야 하는 부분 (함수)에 정확하게 const를 넣어주어야 한다.
"객체의 상태를 변경하지 않는 모든 멤버 함수는 반드시 상수 멤버 함수로 만들어야 한다."
class Rect
{
int xpos, ypos, width, height;
public:
Rect( int x = 0, int y = 0, int w = 0, int h = 0 )
: xpos(x), ypos(y), width(w), height(h) {}
int getArea() const { return width * height;} // 여기에 const를 적어 주어야
// foo에서 getArea사용 가능
// r 이 const 이기 때문에 ~!!
};
//void foo( Rect r ) // call by value 사용하지 않기!
void foo( const Rect& r ) // call by reference : r은 상수 객체
{
int n = r.getArea();
}
int main()
{
Rect r(1, 1, 10, 10); // 일반 객체 (비 상수 객체)
int n = r.getArea();
foo(r);
}
mutable
mutable 멤버 데이타 : 상수 멤버 함수 안에서도 값을 변경할 수 있다.
#include <iostream>
class Point
{
int x, y;
mutable int cnt = 0; // 상수 함수에서 접근/수정 가능하게 mutable 옵션 준다.
public:
Point(int a = 0, int b = 0) : x(a), y(b) {}
void print() const
{
++cnt; // 상수 함수 안에서 mutable 변수의 값은 변경할 수 있다.
std::cout << x << ", " << y << std::endl;
std::cout << cnt << std::endl;
}
};
int main()
{
Point pt(1,1);
pt.print();
pt.print();
}
cnt 값이 증가하는 것을 확인할 수 있다.
상수 객체, 비상수 객체의 우선 순위 및 에러 케이스
아래와 같이 Test 클래스에 foo() 함수가 2개 존재하는데 하나는 const 가 있고, 하나는 const가 없다.
Test t1 객체를 생성해 foo()를 호출하면 1번 foo()가 호출된다.
cosnt Test t2 객체를 생성해 foo()를 호출하면 2번 foo()가 호출된다.
-> 동일 이름의 상수 멤버 함수와 비 상수 멤버 함수를 만들 수 있다.
#include <iostream>
class Test
{
public:
void foo() { // 1
std::cout << "1" <<std::endl;
}
void foo() const { // 2
std::cout << "2" <<std::endl;
}
};
int main()
{
Test t1;
t1.foo(); // 1 번호출, 없으면 2번 호출
const Test t2;
t2.foo(); // 2번 호출, 없으면 error
}
위와 동일한 코드에서 1번 foo()를 주석처리 했을 때, 실행 결과는 아래와 같다.
t1, t2 모두 정상적으로 2번 foo()가 호출된다.
#include <iostream>
class Test
{
public:
//void foo() { // 1
// std::cout << "1" <<std::endl;
//}
void foo() const { // 2
std::cout << "2" <<std::endl;
}
};
int main()
{
Test t1;
t1.foo(); // 1 번호출, 없으면 2번 호출
const Test t2;
t2.foo(); // 2번 호출, 없으면 error
}
하지만 반대로 2번 foo()를 주석처리 했을 때, 실행 결과는 아래와 같이 에러가 발생함을 알 수 있다.
#include <iostream>
class Test
{
public:
void foo() { // 1
std::cout << "1" <<std::endl;
}
//void foo() const { // 2
// std::cout << "2" <<std::endl;
//}
};
int main()
{
Test t1;
t1.foo(); // 1 번호출, 없으면 2번 호출
const Test t2;
t2.foo(); // 2번 호출, 없으면 error
}
main.cpp:20:12: error: passing ‘const Test’ as ‘this’ argument discards qualifiers [-fpermissive]
const 리턴 타입
리턴하는 타입이 const일 때 함수의 리턴 타입 앞에도 const를 넣어 줘야 한다.
아래 코드는 문제 없어 보이지만 에러가 발생한다. 리턴하는 타입이 const int*인데 getData() 앞에 선언된 리턴 타입은 int*로 되어 있기 때문에 conversion 에러가 발생한다.
class Test
{
int data;
public:
int* getData() const
{
// data는 상수이다 : const int data
return &data;
}
};
int main(){}
main.cpp:8:16: error: invalid conversion from ‘const int*’ to ‘int*’ [-fpermissive]
아래와 같이 리턴 타입을 const int* 로 선언하면 문제없이 컴파일이 된다.
class Test
{
int data;
public:
const int* getData() const
{
// 해당 코드처럼 주소값을 리턴하는 코드는 안 좋은 코드임.
// 예시를 위한 코드라는 점!
return &data;
}
};
int main(){}
'C++' 카테고리의 다른 글
[C++] 상속 (Inheritance), protected (0) | 2021.08.25 |
---|---|
[C++] this 포인터 (0) | 2021.08.24 |
[C++] static member (정적 멤버), static 변수, static 함수 (0) | 2021.08.22 |
[C++] 복사 생성자 (copy constructor), 얕은 복사 (Shallow Copy), 깊은 복사 (deep copy) (0) | 2021.08.21 |
[C++] Explicit 생성자 (0) | 2021.08.20 |
댓글