2025. 5. 10. 17:15ㆍProgramming Language/C++
먼저 private와 protected 상속은 사용하는데가 많지 않기에 사용할때에는 많은 고민을 하고 사용하는것이 좋다.
먼저 사용 예제를 보기전에 관계에 대해서 알아보자.
객체들 간의 관계를 말할때 기본적으로 is-a 관계와 has-a 관계가 있다.
이 관계는 클래스 설계 시 상속(inheritance)과 포함(composition)을 어떻게 사용할지를 판단하는 객체 간의 관계 모델링 개념이다
1. is-a 관계 (상속, inheritance)
A는 B이다. → A is a B 로 상속 관계로 표현된다.
그 의미는 자식 클래스가 부모 클래스의 일종이다라는 관계로 공통 동작을 물려받기 위해 사용한다.
class Animal {
public:
void breathe() {}
};
class Dog : public Animal {
public:
void bark() {}
};
라는 코드가 있을때 Dog is an Animal, 개는 동물이다로 Dog는 Animal을 상속받아야 타당한 설계가 된다
그리고 이건 우리가 배워 왔던 public 상속을 의미한다
2. has-a 관계 (구성, composition)
A는 B를 가진다. → A has a B로 멤버 변수로 포함시켜 표현한다.
그 의미는 하나의 클래스가 다른 클래스의 객체를 소유하고 있다는 의미로 구성(composition) 혹은 포함(aggregation) 관계를 의미한다.
class Engine {
public:
void start() {}
};
class Car {
private:
Engine engine; // Car has an Engine
public:
void drive() {
engine.start();
}
};
라는 코드가 있을때 Car has an Engine, 자동차는 엔진을 가진다로 Car는 Engine을 멤버로 가져야 타당한 설계가 된다.
이 두가지 개념을 말한 이유는 public 상속의 경우는 is-a 관계를 가지는데 private, protected 상속의 경우는 has-a 관계를 가지기 때문이다
예제를 하나 만들어 볼건데 이번엔 Queue 클래스를 하나 만들어서 관계를 설명할 것이다.
우선 내부에 vector를 사용하기 위해서 라이브러리를 include해주고
private 접근 지정자로 멤버 변수를 vector로 생성해주자.
그리고 public으로 push(전달한 값을 vector에 넣는 기능)
pop(vector의 가장 마지막 값을 내보내는 기능)
top(vector의 가장 마지막 값을 반환하는 기능)에 대한 함수를 생성해주자.
이 경우에 Queue와 vector멤버 변수 m_vec과 push, pop, top의 경우는 has-a 관계이다.
그런데 여기서 멤버함수 m_vec을 제거한 후에 그냥 vector를 private로 상속을 해준다면
이러면 원래 우리가 알고 있는 상속이라고 할때 부모의 모든 멤버 변수, 상수를 가지고 있고 부모에서 private로 접근 제한이 되어 있더라도 접근이 불가능한거지 자식이 가지고 있기는 하다고 했었다.
그래서
이렇게 궂이 vector로 선언했던 멤버 변수를 사용해서 사용했던 push_back()함수도
이렇게 따로 멤버 변수를 선언하지 않더라고 vector의 기능을 그냥 사용할 수 있다.
다른 함수에서 사용하던 함수들도 동일하다.
여기서 private 상속을 일단 public으로 다시 돌려주고
그리고 이제 Queue를 선언해서 함수를 사용해보면
이렇게 사용했을때
이렇게 원하는 대로 출력이 된다.
그럼 private로 변경해보면
이것도 동일하게 잘 나온다.
그러면 차이가 뭘까?
public으로 선언하면 내가 만들었던 기능 외에 상속한 클래스의 모든 기능을 외부에서 사용할 수 있게 된다.
그런데 private로 선언하면
우리가 만들지 않은 함수들은 외부에서 호출 되지 않는다.
그렇기에 private로 상속을 하는 경우는 외부에서 접근할 수 없으나 상속 받은 클래스 내부에서는 접근이 가능한 형태로, 멤버 변수, 함수와 비슷하게 has-a관계를 가진다고 할 수 있게 되는 것이다.
그리고 이 상속받은 클래스 한테만 열어준 기능으로써 상속받은 클래스를 상속하는 클래스 또한
Queue가 상속받은 vector의 기능을 가지지 않고 Queue본연의 기능만을 가지는것을 볼 수 있다.
그러나 사실은 이렇게 쓰는 것보다 맨 위에 설명했던 멤버 변수로 선언해서 사용하는 것이 훨씬 쉽기 때문에 사용을 지양하는게 좋다.
그럼에도 불구하고 이걸 꼭 사용해야하는 경우가 있는데 멤버 변수로 선언한 객체에 protected로 선언된 함수가 존재하는 경우에는 멤버 변수로 선언해서 그 멤버를 사용할 수 없기 때문에
class Engine {
protected:
void tune(); // protected 함수
};
class Car {
private:
Engine engine; // has-a 관계
public:
void service() {
// engine.tune(); ❌ 접근 불가! (Engine의 protected 멤버는 접근 불가)
}
};
그런 경우에는 private, protected를 사용해서 선언해야하는 경우들이 존재한다.
class Engine {
protected:
void tune(); // protected 함수
};
class Car : protected Engine {
public:
void service() {
tune(); // ✅ protected 상속이므로 접근 가능
}
};
이게 바로 private/protected 상속을 쓰는 유일하게 "정당한" 예 중 하나이다.
이번에는 protected로 상속 받는 경우에 대해서 한번 확인해보자.
먼저 상속 받는 것을 protected로 변경해주고
이 Queue를 상속하는 클래스를 하나 만들어주자.
이렇게 protected로 상속을 하는 경우는 해당 기능을 바로 아래 자식 클래스에서 사용할 수 있게 된다.
이제 이걸 사용해서 우선 순위 큐를 만들어 보자.
먼저 라이브러리에서 알고리즘을 include 해주고
우선순위 큐 클래스를 하나 만들어주자.
그리고 protected로 vector를 상속 받았던 Queue를 public으로 상속 받고 Queue의 모든 기능을 가져와서 오버라이드 해보자.
그리고 push에서는 값을 받으면 Queue의 push를 사용하도록 변경하고
알고리즘의 기능 중 하나인 push_heap를 사용해서 첫번째 인자로 vector의 begin(), 두번째로는 end()를 넣어주자.
(해당 부분은 일단 그냥 그렇구나라고 생각하고 넘어가자.)
std::push_heap(first, last)
마지막에 추가된 요소가 있는 상태의 vector에 대해 그 요소를 힙 조건에 맞게 위로 올려주는 작업(정렬) 을 수행함
함수가 기능하기 위한 전제조건
[first, last - 1] 구간은 이미 힙이어야 하고 last - 1의 값만 새로 추가된 상태여야 함
vector<int> v = {30, 20, 10};
push_heap(v.begin(), v.end()); // 아무 일도 안 함 (이미 힙)
v.push_back(40); // 40 추가 → 힙 조건 깨짐
push_heap(v.begin(), v.end()); // 40을 위로 올림
그리고 pop함수도 동일하게 Queu의 기능으로 변경해주고 pop 이전에 pop_heap을 넣어주자.
(이 또한 일단 그렇구나 하고 넘어가자.)
그리고 이렇게 우선순위 큐의 경우는 가장 앞에 있는 요소가 가장 큰 요소이기 때문에 front()함수를 사용해주자.
이제 객체를 하나 생성해주고 순서대로 값을 100 - 200 - 50을 넣어준 후에 하나 씩 빼면서 마지막 값을 출력해보자.
그러면 기존 Queue 경우는 50 - 200 - 100순서로 나올 것인데
이렇게 우선순위 큐의 경우는 넣는 순서가 아니라 크기에 따라서 순서대로 정렬되어 있음을 볼 수 있다.
추가적으로 상속을 할때 아무런 접근제한자가 없다면 기본적으로 private로 상속이 된다.
그래서 public으로 명확하게 작성해주는 것이 좋다.
그런데 여기서 자식이 struct, 구조체의 경우는 반대로 접근제한자를 생략하면 기본적으로 public 상속을 하게 된다.
이는 이전에 접근 제한자에 대해서 말했다 싶이 class는 없으면 private, struct는 없으면 public으로 이야기 했던 이야기에 포함되는 내용이라고 보면 된다.
'Programming Language > C++' 카테고리의 다른 글
Part2::Ch 03. 상속 - 06. 다중 상속 (0) | 2025.05.10 |
---|---|
Part2::Ch 03. 상속 - 04. 추상 클래스, 순수 가상 함수 (0) | 2025.05.04 |
Part2::Ch 03. 상속 - 03. 정적 결합, 동적 결합 (0) | 2025.05.04 |
Part2::Ch 03. 상속 - 02. 가상 함수 (0) | 2025.05.04 |
Part2::Ch 03. 상속 - 01. 상속의 기본 (0) | 2025.05.04 |