- 8.2.1 캡슐화(Encapsulation)
- 8.2.2 접근 지정자(Access Specifiers)
- 8.2.3 접근 함수(Access Functions or Getter/Setter)
복잡해 보이는 것을 깔끔하게 정리를 하면 뛰어난 프로그래머가 될 수 있다.
8.2.1 캡슐화(Encapsulation)
객체지향 프로그래밍이 가지는 장점 중 하나가 바로 캡슐화이다. 클래스를 사용하면서 멤버 변수와 멤버 함수를 사용하게 되는데, 이들에 대해서 접근 권한을 지정할 수가 있다. 클래스 내부에서만 접근 가능하게 구현한다던가, 어디서든지 접근 가능하게 구현한다던가, 추후에 배울 상속 관계까지만 접근이 가능하도록 할 것인가. 이러한 경우들이 바로 캡슐화에 속하는 이야기이다.
캡슐화를 하는 이유 중 하나는 바로 복잡하고 큰 프로그램일수록 유지보수가 뛰어난 코드로 만들 수 있다는 점이다. 아래의 코드를 예시로 보자.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Date
{
public:
int m_month;
int m_day;
int m_year;
public:
void setDate(const int& month_input, const int& day_input, const int& year_input)
{
m_month = month_input;
m_day = day_input;
m_year = year_input;
}
};
int main()
{
Date today;
today.setDate(8, 4, 2025);
cout << today.m_month << endl;
today.m_month = 1;
today.m_month = -100
return 0;
}
현재 코드는 클래스로 구현을 해서 멤버 함수와 멤버 변수가 모여있지만 어디서든지 접근을 할 수 있도록 접근 권한을 낮춰두었다. main 함수에서도 Date 클래스로 생성한 인스턴스인 today에 대한 멤버 변수를 무작위로 수정할 수 있다. 지금은 코드가 짧아서 모든 상황을 한눈에 볼 수 있지만 실무에서 다루는 코드는 수백수천 줄에 달하며 여러 코드들이 모여있다. 이렇게 어디서든지 중구난방으로 접근하게 둔다면 추후에 클래스 멤버를 수정할 일이 생기면 여러 곳에서 문제가 생기게 된다.
8.2.2 접근 지정자(Access Specifiers)
앞의 상황을 해결해주기 위해 접근 지정자를 사용하여 캡슐화를 제대로 사용할 수 있다. 흔히 알고 있는 감기 알약 같은 경우 우리는 감기를 견뎌내기에 좋은 기능을 하는 정도만 알지 실제로 캡슐이 어떤식으로 몸속에서 작용하는지는 알지 못한다. 이처럼 결과적인 것만 알게 해주고 내부를 감추는 것이 캡슐화의 특성 중 하나가 되며, 이를 프로그래밍적으로 구현한 것이 접근 지정자이다.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Date
{
int m_month;
int m_day;
int m_year;
public:
void setDate(const int& month_input, const int& day_input, const int& year_input)
{
m_month = month_input;
m_day = day_input;
m_year = year_input;
}
};
int main()
{
Date today;
today.setDate(8, 4, 2025);
return 0;
}
이전 코드에서도 사용했지만 클래스 내부에 public 키워드가 바로 접근 지정자이다. public으로 지정할 경우 그 아래의 멤버들은 전부 해당 클래스의 인스턴스에 접근할 수 있는 코드 내에서 모두 사용할 수 있게 된다.
반면 클래스의 멤버 변수는 public 키워드를 작성해주지 않았는데 이 경우 자동으로 private 키워드로 설정된다. 클래스가 구조체와 다른점 중 하나가 바로 기본 설정이 private 키워드라는 점이다. 키워드 이름에서도 알 수 있듯이 접근에 제한이 생기는데 해당 클래스에 속하는 멤버 이외에는 private 멤버에 접근할 수 없게 된다. 같은 클래스 멤버라면 public이라도 private 멤버에 접근할 수 있다.
그럼 접근 제한이 걸린 멤버는 외부에서 어떻게 활용할 수 있을까?
8.2.3 접근 함수(Access Functions or Getter/Setter)
멤버 변수에 대한 세팅은 setter라 불리는 접근 함수로 구현을 한다. 외부에서 매개변수로 값들을 받아 필요한 멤버 변수를 설정해주는 것이다. 추후에 배울 생성자를 통하여 해당 기능을 초기화하는 것처럼 구현할 수 있다.
#include <iostream>
#include <string>
using namespace std;
class Date
{
int m_month;
int m_day;
int m_year;
void setDate(const int& month_input, const int& day_input, const int& year_input)
{
m_month = month_input;
m_day = day_input;
m_year = year_input;
}
const int& getDay() // getters
{
return m_day;
}
void copyFrom(const Date& original)
{
m_month = original.m_month;
m_day = original.m_day;
m_year = original.m_year;
}
};
int main()
{
Date today;
today.setDate(8, 4, 2025);
cout << today.getDay() << endl;
Date copy;
copy.copyFrom(today);
cout << copy.getDay() << endl;
return 0;
}
setDate 함수를 통하여 멤버 변수를 설정해주었다. 하지만 멤버 변수는 private이기 때문에 외부에서 해당 값을 들여다보지 못한다. 이를 위해서 getter라고 부르는 접근 함수를 멤버 변수로 만들어 준다. 결론적으로 getDay라는 함수를 통해서 private 멤버 변수인 m_day의 값을 불러올 수 있다.
getDay 함수는 값을 복사하도록 int로 return 값을 설정할 수 있지만 복사하는 것이 마음에 들지 않는다면 const와 참조 연산자 &를 함께 사용하여 return 값으로 설정해주자. 참조 연산자를 통해 해당 변수에 직접 접근이 가능해지지만 이는 private 키워드를 무시하게 되는 효과를 가져오므로 const를 통해서 바꾸는 것을 차단하는 것이다.
또한 같은 클래스에서 생성된 인스턴스의 경우 자신의 멤버 함수로 다른 인스턴스에 대한 private 멤버들에 접근을 할 수 있게 된다.