접근 지정자란
접근 지정자란 클래스의 외부나 클래스를 상속받은 클래스에서 그 클래스의 멤버 변수나 함수에 접근 가능한지를 지정하는 키워드입니다.
접근 지정자는 public, protected, private가 있습니다.
public 접근 권한을 받은 클래스 멤버는 외부나 상속받은 클래스에서 접근이 가능합니다.
protected 접근 권한을 받은 클래스 멤버는 상속받은 클래스만이 접근 가능합니다.
private는 접근 권한을 받은 클래스 멤버는 멤버를 갖고 있는 클래스만이 접근 가능합니다.
접근 권한 범위를 부등식으로 표현하면 다음과 같습니다.
public > protected > private
예제를 살펴보겠습니다.
class A{
int m_Private; // 접근 지정자를 사용하지 않으면 private가 기본값 입니다.
protected:
int m_Protected;
public:
int m_Public;
};
int main(){
A obj;
obj.m_Private = 100; // error
obj.m_Protected = 100; // error
obj.m_Public = 100; // ok
return 0;
}
외부에서는 클래스의 public 멤버만 접근이 가능하므로, protected, private 멤버에 접근하면 오류가 발생합니다.
그리고, 접근 지정자를 사용하지 않으면 기본적으로 private 지정자를 사용한 것이 됩니다.
그래서, m_Private는 private 키워드를 사용하지 않았지만, private 멤버입니다.
이 접근 지정자를 이용해서, 클래스를 직접 생성할 수 없게 만들 수도 있습니다.
class A{
protected:
A(){} // protected 생성자
};
class B : protected A{
};
int main(){
A obj; // error!! 객체를 생성할 수 없습니다.
B derived; // ok
return 0;
}
A의 생성자 A()가 protected 멤버 함수로 지정되었기 때문에, 외부에서 생성자에 접근할 수 없습니다.
따라서, A클래스의 객체를 생성할 수 없게 되는 것입니다.
이를 이용하면 추상 클래스와 마찬가지로 상속받은 클래스만이 객체를 생성하도록 제어할 수 있습니다.
추상 클래스에 관심 있는 분은 여기를 봐주세요.
참고로, 클래스 B는 객체를 만드는데 문제가 없습니다.
B는 생성자를 하나도 작성하지 않았으므로, 컴파일러가 기본 생성자를 만들기 때문입니다.
만약, 클래스 A에서 생성자 A()를 private로 선언하면, 클래스 B도 A를 상속할 수 없습니다.
상속 시 접근 지정자
클래스 상속 시에도 접근 지정자를 사용해서, 상속받는 클래스를 통한 클래스 멤버의 접근 여부를 제한할 수 있습니다.
표현이 비슷한 것 때문에, 오해가 생길 수 있는데, 상속받는 클래스 B는 상속하는 클래스 A의 선언부에 있는 접근 지정자를 따르지만, 클래스 B의 외부나 B에서 상속받은 클래스 C는 B를 통해 클래스 A의 멤버에 접근할 때, 상속 시 사용한 접근 지정자를 따른다는 얘깁니다.
글보다는 코드를 직접 보는 것이 더 나을 것 갔습니다.
class A{
public:
int m_Public;
protected:
void ProtectedFunc(){}
};
// A에서 바로 상속받은 클래스는 상속 시 지정자에 의한 제한이 없습니다.
// private 지정자에 영향을 받지 않습니다.
class B : private A{
public:
void PublicFunc(){
ProtectedFunc(); // A 클래스에서 protected. ok
m_Public = 10; // A 클래스에서 public. ok
}
};
class C : public B{ // B를 통해 A의 private 멤버를 사용하기 때문에 제한
public:
void PublicFunc(){
ProtectedFunc(); // B 클래스에서 private. error!!
m_Public = 10; // B 클래스에서 private. error!!
}
};
int main(){
A obj;
obj.m_Public = 10; // A 클래스에서 public. ok
obj.ProtectedFunc(); // A 클래스에서 protected. error!!
B derived;
derived.ProtectedFunc(); // B 클래스에서 private. error!!
derived.m_Public = 20; // B 클래스에서 private. error!!
return 0;
}
클래스 B는 private 지정자를 통해, A 클래스를 모든 멤버를 private 멤버로 제한했습니다.
따라서, 클래스 C는 A 클래스의 모든 멤버에 접근할 수 없습니다.
마찬가지로, 외부에서도 B 클래스를 통해 A 멤버에 접근할 순 없습니다.
만약, 상속 시 접근 지정자를 사용하지 않으면 private 상속을 한 것과 같습니다.
이것은 클래스 내에서 접근 지정자를 사용하지 않으면 private 지정자를 사용한 것으로 보는 것과 같습니다.
참고로, 구조체(struct)나 공용체(union)에서도 접근 지정자를 사용할 수 있는데, 이 경우 접근 지정자를 생략하면 public 지정자를 사용한 것과 같습니다.
가상 함수의 접근 지정자
가상 함수에 접근 제한은 일반 함수와 마찬가지로 객체를 참조하는 클래스의 접근 제어를 따릅니다.
기본(base) 클래스 타입의 참조(reference)에서 가상 함수를 호출하면, 실제로 가리키는 클래스의 접근이 불가능한 가상 함수라도, 기본 클래스에서 접근이 가능한 가상 함수라면 호출이 가능합니다.
#include <iostream>
using namespace std;
class A{
public:
virtual void VFunc(){
cout << "Base VFunc called\n";
}
};
class B : public A{
private:
virtual void VFunc(){ // private 가상 함수
cout << "Derived VFunc called\n";
}
};
int main(){
B derived;
A& rObj = derived;
rObj.VFunc(); // ok
//derived.VFunc(); // 접근 불가 error!!
return 0;
}
▼출력
Derived VFunc called
이러한 접근 제어를 통해, 반드시 기본(base) 타입의 참조로 가상 함수를 사용하도록 강제할 수도 있을 겁니다.
이 글과 관련 있는 글들
'C, C++ > C++ 언어' 카테고리의 다른 글
[C++] 좌측값( l-value )과 우측값( r-value ) 구분하기 (0) | 2024.06.23 |
---|---|
[C++] friend 키워드 사용법 ( friend 함수, 클래스 ) (0) | 2024.05.30 |
[C++] 추상 클래스와 순수 가상 소멸자 (0) | 2024.05.25 |
[C++] 동적 바인딩과 가상 함수 테이블 ( V-Table ) (0) | 2024.05.24 |
[C++] 가상 함수( Virtual Function )를 사용하는 이유 (0) | 2024.05.23 |