멤버로의 접근 제한
예제 12-1에서는 데이터 멤버에 주소와 평수, 전화번호의 값을 대입하고 있다. 이것에 의해 실제의 집에 주소와 평수, 전화번호를 설정하는 처리를 수행하고 있는 것이다. 집의 주소를 [부산시 남구 대연3동], 평수를 [64], 전화번호를 [051-629-0001]로 설정한다.
그런데 사실은 이것만으로는 문제가 발생해 버리는 경우가 있다. 예를 들면, 예제 12-1의 main() 함수 내에서는 다음과 같은 기술이 가능하다는 것에 주의해야 한다.
int main()
{
House my_house;
my_house.address = "부산시 남구 대연3동";
my_house.space = -32; //잘못된 평수를 대입하고 있다.
my_house.telephone = "051-629-0001";
my_house.show();
return 0;
}
이 코드는 무엇을 의미하고 있는 것인가? 지금까지와 똑같이 고려하면 이 코드 내에는
my_house 집의 평수는 -32로 한다.
는 것이 수행되고 있는 것이다.
그러나 집에서는 [평수를 마이너스로 한다]는 것은 있을 수 없다.
클래스는 [단일]의 개념을 중심으로 설계하고 있다. 그러므로 복잡한 코드를 작성해가는 중에 이와 같은 부자연스러운 조작함으로써 프로그램에 오류가 발생해 버리는 경우가 있다.
통상 클래스를 설계할 때에는 이와 같이 좋지 않은 상태가 발생하지 않도록 여러 가지 구조를 사용하기도 한다. 이로부터 그 구조를 하나하나 살펴보도록 한다.
(1) private 멤버의 생성
예제 12-1에서 평수가 마이너스로 되어 버리는 오류의 원인은 무엇일까? 이 원인은 멤버를 무제한으로 이용하여 마음대로 값(여기서는 -32)을 대입해 버린 것에 있다.
C++에서는 이와 같은 잘못이 발생하지 않도록 클래스의 바깥에서 마음대로 접근할 수 없는 멤버로 해 두는 것이 가능하다. 이 멤버를 private 멤버라고 한다.
class House{
private:
const char* address;
int space;
const char* telephone;
//데이터 멤버는 private 멤버로 한다.
...
};
여기서는 멤버에 private이라는 지정자를 붙였다. 이렇게 하면 House 클래스의 바깥(main() 함수)에서 데이터 멤버에 접근할 수 없게 되는 것이다.
int main()
{
...
//클래스 바깥에서 private 멤버에는 접근할 수 없다.
my_house.address = "부산시 남구 대연3동";
my_house.space = -32
my_house.telephone = "051-629-0001";
}
이것으로 집의 평수에 마이너스의 값이 대입되는 것은 없게 된다.
(2) public 멤버의 생성
이와 같이 데이터 멤버를 private 멤버로 하면 클래스 바깥에서 접근할 수 없게 된다.
그러나 이와 같이 함으로써 main() 함수 내에서 집의 주소와 평수, 전화번호를 설정할 수 없게 되어 버리는 것일까?
사실은 접근하는 방법이 있다. 예제 12-2에서 살펴보도록 한다.
예제 12-2) public 멤버를 선언하여 멤버에 접근
private 멤버를 선언하면 클래스 바깥에서 접근할 수 없지만, 예제 12-1을 개선하여 이것을 setSpace() 멤버 함수에 public을 지정하여 main() 함수 내에서 집의 주소, 평수, 전화번호의 값을 설정하는 코드를 작성해 보도록 한다. 단, 평수의 값은 0 이상 2000 이하로 한다.
#include <iostream>
using namespace std;
class House{ //...①
private:
const char* address;
int space;
const char* telephone;
public:
void show();
void setSpace(const char* add, int space_num, const char* tel);
};
void House::show() //...②
{
cout << "집의 주소는 " << address << "입니다.\n";
cout << "집의 평수는 " << space << "평입니다.\n";
cout << "집의 전화번호는 " << telephone << "입니다.\n";
}
void House::setSpace(const char* add, int space_num, const char* tel) //...③
{
if (space_num > 0 && space_num < 2000) { //전달된 값을 조사해서...
address = add;
space = space_num;
telephone = tel;
//올바르면 값을 설정한다.
cout << "주소는 " << address << ", 평수는 " << space << "이고, 전화번호는 "
<< telephone << "로 하였다.\n";
}
else {
//잘못된 값을 설정할 수 없도록 하고 있다.
cout << space_num << "은(는) 올바른 평수가 아니다.\n";
cout << "평수를 변경할 수 없다.\n";
}
}
int main()
{
House my_house;
//my_house.address = "부산시 남구 대연3동"; ...④
//my_house.space = -32; ...④
//my_house.telephone = "051-629-0001"; ...④
my_house.setSpace("부산시 남구 대연3동", 64, "051-629-0001"); //...⑤
my_house.show();
cout << "올바르지 않는 평수를 (-34)로 지정해 본다.\n";
my_house.setSpace("부산시 남구 대연3동", -34, "051-629-0001");
my_house.show();
return 0;
}
① House 클래스의 선언을 수행한다.
② House 클래스의 멤버 함수를 정의한다.
③ setSpace 멤버 함수를 새로 추가하여 주소와 평수, 전화번호를 설정한다. 여기서는 평수가 올바른가를 체크(평수가 0보다 크고 2000 이상인 값)하고 나서 데이터 멤버에 값을 대입하는 처리를 하고 있는 것에 주의해야 한다.
④ private 멤버로는 접근할 수 없다.
⑤ ④와 같이 House 클래스 바깥에서 주소와 평수, 전화번호를 직접 설정할 수 없는 것을 setSpace() 멤버 함수이면 호출할 수 있다. setSpace() 멤버 함수는 반드시 public 멤버를 호출해서 값을 설정한다. 이 멤버 함수를 사용하면 올바른 값이 있는가를 반드시 체크하기 때문에 평수가 설정된다. 즉, 잘못된 평수가 설정되는 것이 없게 된다.
setSpace() 멤버 함수에는 public이라는 지정을 붙이고 있다. 이 멤버를 public 멤버라고 한다. public을 붙인 멤버는 클래스 바깥에서 이용할 수 있다. 이렇게 해서 private과 public을 사용함으로써 올바른 주소와 평수, 전화번호를 설정할 수 있는 것이다.
(3) 캡슐화
클래스 내에 데이터(데이터 멤버)와 기능(멤버 함수)을 하나로 하여 할당하고 싶은 멤버에 private을 붙여 마음대로 접근할 수 없게 하는 기능을 캡슐화(encapsulation)라고 한다. 즉, 데이터를 캡슐로 싸서 외부의 접근으로부터 데이터를 보호하는 객체 지향의 특성이다. 일반적으로는 예제 12-2와 같이
데이터 멤버 → private 멤버
멤버 함수 → public 멤버
로 지정하는 것이 자주 수행되고 있다. 캡슐화는 클래스가 가지는 가장 중요한 기능의 하나이다.
(4) 멤버 함수를 인라인 함수로 사용
여기서 하나 주의를 해야 하는 것은 private 멤버에 접근하려면 반드시 public 멤버 함수를 호출해서 접근하지 않으면 안 된다. 이 때문에 이와 같은 클래스를 이용할 때에는 빈번하게 멤버 함수가 호출될 가능성이 있다. 그러나 함수를 자주 호출하면 실행 속도가 지연되는 경우가 있다. 그래서 간단한 멤버 함수에 관해서는 인라인 함수로 하는 것이 자주 수행된다. 다음의 코드를 살펴보도록 한다.
예제 12-3) 인라인 함수의 정의
예제 12-2의 House 클래스에서 인라인 함수를 사용하여 멤버 space를 출력하는 코드를 작성하도록 한다.
#include <iostream>
using namespace std;
class House{
private:
const char* address;
int space;
const char* telephone;
public:
void getSpace()
{
cout << "집의 평수는 " << space << "입니다.\n";
} //인라인 함수를 정의한다. 멤버 함수의 본체를 클래스 내에 정의하면 인라인 함수로 된다.
void show();
void setSpace(const char* add, int space_num, const char* tel);
};
void House::show()
{
cout << "집의 주소는 " << address << "입니다.\n";
cout << "집의 평수는 " << space << "평입니다.\n";
cout << "집의 전화번호는 " << telephone << "입니다.\n";
} //클래스의 바깥에 정의되어 있는 멤버 함수는 통상의 함수
void House::setSpace(const char* add, int space_num, const char* tel)
{
if (space_num > 0 && space_num < 2000) {
address = add;
space = space_num;
telephone = tel;
//올바르면 값을 설정한다.
cout << "주소는 " << address << ", 평수는 " << space << "이고, 전화번호는 "
<< telephone << "로 하였다.\n";
}
else {
//잘못된 값을 설정할 수 없도록 하고 있다.
cout << space_num << "은(는) 올바른 평수가 아니다.\n";
cout << "평수를 변경할 수 없다.\n";
}
}
//main() 함수에서 House 클래스로부터 객체를 선언하였다.
//실행 결과로부터 멤버 함수와 인라인 함수의 실행 결과를 출력할 수 있다.
int main()
{
House my_house;
cout << "setSpace() 함수 호출 => ";
my_house.setSpace("부산시 남구 대연3동", 64, "051-629-0001");
cout << "getSpace() 함수 호출 => ";
my_house.getSpace();
cout << "show() 함수 호출 => ";
my_house.show();
return 0;
}
이 코드는 House 클래스 코드의 일부이다. 함수 파트에서와 같이 함수를 인라인 함수로 한 경우는 inline을 지정한다. 그러나 멤버 함수의 경우는 더욱 간단하게 인라인 함수를 정의할 수 있다. 클래스 선언 내에 함수 본체를 정의하면 자동적으로 인라인한 것으로 된다.
상기의 코드의 getSpace() 함수는 클래스 선언 내에 정의되어 있기 때문에 자동적으로 인라인 함수로 된다. 한편, 클래스 선언의 바깥에 정의한 show(), setSpace() 함수는 통상의 함수로 된다.
출처: 박흥복, 서정희. 2015. C++ 프로그래밍 (초보자를 위한). 문운당
'개발 > C++' 카테고리의 다른 글
C++ 생성자와 정적 멤버 (0) | 2022.05.12 |
---|---|
C++ 클래스와 객체3 (0) | 2022.05.12 |
C++ 클래스와 객체 (0) | 2022.05.07 |
C++ 열거형, 구조체, 공용체2 (0) | 2022.05.04 |
C++ 열거형, 구조체, 공용체 (0) | 2022.05.04 |