접근 지정자(Access Modifier)는 객체 지향 프로그래밍에서 데이터 은닉(data hiding)을 구현하기 위해 사용되는 핵심 도구입니다. 클래스 외부에서 내부 데이터에 함부로 접근하지 못하도록 통제함으로써, 코드의 안정성과 캡슐화를 보장할 수 있습니다.
C++에는 세 가지 접근 지정자가 존재하며, 각각 다음과 같은 특징을 가집니다.
- public — 어디서든 접근 가능
- private — 클래스 내부에서만 접근 가능
- protected — 클래스 내부와 파생(자식) 클래스에서 접근 가능
아래에서 각 접근 지정자를 예제 코드와 함께 자세히 살펴보겠습니다.
1. public 접근 지정자
public으로 선언된 데이터 멤버와 멤버 함수는 프로그램의 어디에서나 접근할 수 있습니다. 다른 클래스에서도 접근이 가능하며, 클래스 객체와 점(.) 연산자를 사용하면 언제든 호출할 수 있습니다.
예제 코드
#include<iostream>
using namespace std;
class Add {
public:
int a, b;
void sum() {
cout<<"Sum of "<< a <<" and "<< b <<" is "<<a+b;
}
};
int main() {
Add obj;
obj.a = 2;
obj.b = 5;
obj.sum();
return 0;
}실행 결과
Sum of 2 and 5 is 7
위 프로그램에서 Add 클래스는 두 개의 public 데이터 멤버 a와 b를 가지고 있으며, 멤버 함수 sum()은 이 두 값의 합을 출력합니다.
class Add {
public:
int a, b;
void sum() {
cout<<"Sum of "<< a <<" and "<< b <<" is "<<a+b;
}
};main() 함수 안에서 Add 클래스의 객체를 생성한 뒤, a와 b에 직접 값을 대입합니다. 이것이 가능한 이유는 두 변수가 public으로 선언되어 클래스 외부에서도 접근할 수 있기 때문입니다. 마지막으로 sum()을 호출하여 합계를 출력합니다.
Add obj; obj.a = 2; obj.b = 5; obj.sum();
2. private 접근 지정자
private으로 선언된 데이터 멤버는 클래스 내부의 멤버 함수에서만 접근할 수 있습니다. 클래스 외부의 어떤 함수도 직접 접근할 수 없으며, 예외적으로 프렌드(friend) 함수만이 private 멤버에 접근할 수 있습니다.
예제 코드
#include<iostream>
using namespace std;
class Add {
private:
int a, b;
public:
void setdata(int x, int y) {
a = x;
b = y;
}
void sum() {
cout<<"Sum of "<< a <<" and "<< b <<" is "<<a+b;
}
};
int main() {
Add obj;
obj.setdata(9,5);
obj.sum();
return 0;
}실행 결과
Sum of 9 and 5 is 14
위 프로그램에서 Add 클래스의 a와 b는 private 변수로 선언되었습니다. 따라서 외부에서 직접 값을 설정할 수 없으며, 대신 public 멤버 함수인 setdata()를 통해 값을 초기화해야 합니다. sum()은 두 값의 합을 출력합니다.
class Add {
private:
int a, b;
public:
void setdata(int x, int y) {
a = x;
b = y;
}
void sum() {
cout<<"Sum of "<< a <<" and "<< b <<" is "<<a+b;
}
};main() 함수에서는 객체를 생성한 후, private 변수인 a와 b를 초기화하기 위해 반드시 setdata()를 호출해야 합니다. 만약 obj.a = 9;처럼 직접 접근하려 하면 컴파일 오류가 발생합니다. 이후 sum()을 호출하여 결과를 출력합니다.
Add obj; obj.setdata(9,5); obj.sum();
3. protected 접근 지정자
protected로 선언된 데이터 멤버는 private과 매우 유사하게 동작합니다. 클래스 외부에서는 직접 접근할 수 없지만, 결정적인 차이점은 기반(base) 클래스의 파생(derived) 클래스에서 접근할 수 있다는 것입니다. 상속 관계에서 유용하게 활용됩니다.
예제 코드
#include<iostream>
using namespace std;
class Parent {
protected:
int a, b;
};
class Child: public Parent {
public:
void getdata(int x, int y) {
a=x;
b=y;
}
void putdata() {
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
}
};
int main() {
Child obj;
obj.getdata(9,1);
obj.putdata();
return 0;
}실행 결과
a = 9 b = 1
위 프로그램에서 부모 클래스인 Parent는 두 개의 protected 변수 a와 b를 가지고 있습니다.
class Parent {
protected:
int a, b;
};자식 클래스인 Child는 getdata()와 putdata()라는 두 개의 함수를 통해 a와 b의 값을 각각 설정하고 출력합니다. 이것이 가능한 이유는 a와 b가 protected 변수이고, Child가 Parent의 파생 클래스이기 때문입니다. 만약 Child가 아닌 외부 코드에서 접근했다면 컴파일 오류가 발생했을 것입니다.
class Child: public Parent {
public:
void getdata(int x, int y) {
a = x;
b = y;
}
void putdata() {
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
}
};main() 함수에서는 Child 클래스의 객체 obj를 생성한 후, getdata()로 값을 설정하고 putdata()로 출력합니다.
Child obj; obj.getdata(9,1); obj.putdata();
마무리: 세 가지 접근 지정자 비교
| 접근 지정자 | 클래스 내부 | 파생 클래스 | 클래스 외부 |
|---|---|---|---|
| public | O | O | O |
| protected | O | O | X |
| private | O | X | X |
적절한 접근 지정자를 선택하는 것은 캡슐화를 실현하고 안전하고 유지보수하기 좋은 C++ 코드를 작성하는 첫걸음입니다. 기본적으로는 멤버 변수를 private 또는 protected로 숨기고, 필요한 인터페이스만 public으로 노출하는 것이 권장됩니다.