C++/C++ : Study

상속(4) - protected 멤버

더블유제이플로어 2025. 3. 24. 06:21

Inheritance

◆  상속의 정의
◆  유도 클래스
◆  protected 멤버
◆  상속에서의 생성자와 소멸자
◆  기본 클래스의 생성자와의 관계
◆  상속과 멤버 함수

protected 멤버에 대해 알아보자.


Protected Member

◆ Protected 멤버

   ● 기본 클래스에서 접근 가능
   ● 유도 클래스에서 접근 가능
   ● 기본 또는 유도 클래스의 객체로부터는 접근 불가능
        ☞ private. 클래스에서는 접근 가능하고, 객체로부터는 접근이 불가능하다.
        ☞  protected 는 "상속이 이뤄지는 private" 라고 생각하면 편하다.
        ☞  private 는 유도 클래스에서 접근이 불가능함에 유의하자
              ▶ private 은 상속과 관계없이 무조건 (자신) 클래스 내부에서만 접근 가능하다.

class Base {
protected:
	// protected member
};

class 를 설명할때 access-specifier 접근제한자 이야기를 하면서
public , private 언급을 하였고,
protected 는 나중에 상속에서 배울거라고 언급하였다.

상속과 관련해서는 protected 접근 제한자를 멤버 변수나 멤버 함수에 걸어둘 수 있다.
protected 는 어떻게 동작하냐면

기본 클래스에서 접근이 가능하다.
유도 클래스에서도 접근이 가능하다.
기본 또는 유도 클래스의 객체로부터는 접근이 불가능하다.

class 에서는 접근이 가능하고 객체에서는 접근이 불가능한것이 원래는 private 이다.

#include <iostream>
using namespace std;

class Entity {
protected:
	int x, y;
public:
	Entity(int x, int y)
		:x{ x }, y{ y } { }
};

class Player : public Entity
{
private:
	int hp;
	int xp;
	int speed;
public:
	Player(int x, int y, int speed)
		:Entity{ x,y }, speed{ speed } {
	}
	void Move(int dx, int dy)
	{
		x += dx; y += dy;
	}
	void SetHP(int hp)
	{
		this->hp = hp;
	}
};
int main()
{
	Player p{ 5,5,10 };
	p.HP = 10; // 컴파일 에러! private 로 되어 있기 떄문에 객체에서는 접근을 못하게 되어있다.
}
void SetHP(int hp)
{
	// HP 객체로는 값을 변경할 수 없기때문에 멤버 함수를 사용하여 변경한다.
	if (hp < 0) // 음수인 값은 대입을 못하게 막아둔다.
	{
		return; 
	}
	this->hp = hp; // 음수가 아닌 경우에만 hp 값을 변경한다.
}

음수인 경우에는 return 하고 음수가 아닌 경우만  수정할 수 있도록 통로를 만들어 주는것이 안전하다.
private 에 존재 의미이다.
여기까지는 기존 class 에서 배웠던 내용이다.

객체에서는 접근 불가능하고, 클래스에서만 접근 가능한 애들이 private 이다.

protected 멤버도 비슷하다.
클래스에서는 접근 가능하지만 객체에서는 접근 불가능하다.

그럼 private 와 다른점은 무엇일까?
protected 은 상속이 이루어지는 private 라고 생각해봐야한다.

class Entity 에 멤버 변수는
protected int x, int y 로 되어있기 때문에
Player 클래스에서도 x 와 y에 값을 변경할 수 있었다.
(Player 클래스 Move 참고)

int main()
{
	Player p{ 5,5,10 };
	p.Move(1, -1); // 6,4 로 변한다.
}

Player 클래스의 멤버 함수인 Move() 안에서 
x, y 에 접근할 수 있다.
x, y 는 Entity 클래스에 선언되어 있다.
Entity 의 멤버도 Player 에 포함이 되기 때문에
접근해서 값을 바꿀 수 있었다.

x, y가 private 으로 선언하였다면
Player::Move() 에서 x 와 y 를 선언할 수 없다.

class Entity {
private:
	int x, y;
public:
	Entity(int x, int y)
		:x{ x }, y{ y } { }
};

 

x, y 는 잠김 상태가 된다.

// protected 는 클래스에서만 사용 가능하고 객체는 사용 못하기 때문에
int main()
{
    Entity e{1,1};
    e.x = 10; // 컴파일 에러!
	Player p{ 5,5,10 };
	p.x = 10; // 컴파일 에러!
}

protected 와 private 의 차이를 잘 알아두어야 한다.

private 은 유도 클래스에서도 접근이 불가능하며 상속과 관계없이 무조건 클래스 내부에서만 접근 가능하다.


중요한 Part이다.

◆ Protected 멤버 변수 

class Base
{
public:
	int a;
protected:
	int b;
private:
	int c;
};

Base 클래스를 만들었고
public 변수 a
protected 변수 b
private 변수 c 가 있고
유도 클래스를 따로 만들었다.

유도 클래스 입장에서
멤버 변수 a 는 여전히 public 이다.
Drived d;
d.a = 10; // 만들 수 있다. public  이기 때문이다.

멤버 변수 b 는 protected 이기 때문에
d.b = 10; // 컴파일 에러가 난다.
대신 Drived 클래스 내부에서 멤버 변수 b 를 바꿀 수 있다.

멤버 변수 c 는 접근 불가이다.
멤버 변수 c 는 기본 클래스에서만 접근할 수 있는 private 이기 때문에
유도 클래스에서 접근이 불가능하다.
Drived 클래스 내부에서도 멤버 변수 c 를 바꿀 수 없다.


고급 Part이다.

class Derived : public Base
{
public:
	void SetValue()
	{
		cout << a << endl; // OK
		cout << b << endl; // OK
		cout << c << endl; // ERROR!
	}
};

int main()
{
	Derived d;
	d.a = 1; //OK
	d.b = 2; // ERROR!
	d.c = 3; // ERROR!

	cout << sizeof(Base) << endl; // 12 Bytes
	cout << sizeof(Derived) << endl; // 12 Bytes
}

접근 할 수 없는 것이지, 값이 메모리에 저장되지 않는 것은 아닙니다.

Derived d 변수에
멤버 변수 a , b , c 가 모두 포함은 되어있다.
다만 값을 변경할 수 있는건 public 인 멤버 변수 a 이다.

멤버 변수 b 는 protected 이기 때문에
클래스 Derived 에서만 접근이 가능하고
객체에서는 접근이 불가능 하다.
당연히 멤버 변수 c 도 접근 불가능 하다.

sizeof 함수를 사용해서
객체가 차지하는 크기를 알 수 있다.
sizeof(타입) 을 선언하게 되면 타입이 얼만큼 크기를 차지하는지도
알 수 있었다.
sizeof(int) 경우 4 byte 이기 때문에 4가 출력된다.

sizeof(Base) 와 sizeof(Derived) 인 경우에는 12 byte 를 차지한다.
왜 12 byte 를 차지할까?

int 는 4 byte 를 차지한다.
그리고 실제로 어떤 클래스 객체를 만들게 되면은
멤버 변수들이 메모리 공간에 저장이 된다.

int 멤버 변수 3개를 가지고 있는 Base 와 Derived 클래스 내에 메모리 공간은 다음과 같다.

sizeof(Base) 와 sizeof(Derived) 메모리 공간

유도 클래스인 Derived 는 Base 멤버 객체를 가지고 있으면서 , 멤버 변수를 선언하지 않아서 12 byte 가 나온다.

왜 이 part 가 고급이냐면 헷갈릴수 있기 때문이다.
유도클래스 객체 d 에서 
proteced 멤버 변수나 b나 provate 멤버 변수 c 에 접근할 수 없으나
접근할 수 없다고 해서 메모리 공간을 차지 안한다는 의미는 아니다.


Inheritance

◆  상속의 정의 : 기본 클래스를 기반으로 새 클래스를 만드는 기능
◆  유도 클래스 : 그렇게 만들어진 클래스를 유도 클래스라고 하며, 기본 클래스의 모든 것이 포함되어 있다.
◆  protected 멤버 : 상속에서 public 은 그대로, private 은 여전히 private 이므로, 정보를 숨기되 상속 계층의 하위로 데이터를 상속하고 싶을때는 protected 를 사용.
◆  상속에서의 생성자와 소멸자
◆  기본 클래스의 생성자와의 관계
◆  상속과 멤버 함수

요약하자면
상속에서 public 은 그대로 public 이고,
private 는 private 이나, (자기 자신 클래스에서만 사용할 수 있다.)
객체에서는 사용할 수 없게 정보를 숨기되,
유도 클래스에서 접근하고 싶을때 protected 를 사용한다.


https://youtu.be/7zjvmceonXg?si=JsH2xmZYITyr06H_


예시코드가 없어서 오늘도 AI Claude 에게 protected 멤버 함수에 대해서 소스 코드와 설명을 요청하였다.

#include <iostream>
#include <string>
using namespace std;

class Person {
protected: // protected 멤버 변수
	string name;
	int age;

public:
	// 생성자
	Person(string n, int a):name(n) , age(a){}

	// protected 멤버에 접근할 수 있는 protected 메서드
protected:
	void displayBasicInfo() {
		cout << "이름: " << name << ", 나이: " << age << endl;
	}
};

class Student : public Person {
private:
	string studentId;
	
public:
	// 생성자
	Student(string n, int a, string id)
		: Person(n, a), studentId(id) { }

	// 부모 클래스의 protected 멤버를 사용하는 메서드
	void showStudentInfo() {
		// protected 멤버인 name 과 age에 직접 접근 가능
		displayBasicInfo();
		cout << "학번: " << studentId << endl;
	}
};

int main()
{
	Student student("김영희", 20, "2025001");

	// 상속된 protected 멤버 사용 예시
	student.showStudentInfo();

	// 직접 접근 불가능
	//cout << student.name; // 컴파일 에러

	return 0;
}

1. protected 멤버의 특징
   private 보다 더 넓은 접근 범위를 가진다.
   해당 클래스와 이를 상속받은 자식 클래스 내에서만 접근 가능하다.
  외부에서는 직접 접근 할 수 없다.

2. 예제 설명
   Person 클래스에서 name  과 age 를 protected 로 선언.
   Student 클래스는 Person 를 상속받아 protected 멤버에 직접 접근 할 수 있다.
   showStudentInfo() 멤버 함수에서 protected 멤버를 사용하고 있다.
  
주요 포인트
   protected 멤버는 상속관계에서 유용한다.
   자식 클래스에서 부모 클래스의 protected 멤버를 자유롭게 사용할 수 있다.
   외부에서는 접근할 수 없어 데이터 은닉을 제공한다.

고마워 claude !