프로그래밍 언어/C++

[게임 프로그래머 입문 올인원] 객체지향 : OOP 3대 요소 / 상속성, 은닉성, 다형성 (28, 29, 30강)

순정법사 2023.09.11

B. OPP 3대 요소

1. 상속성 (inheritance)

a. 상속성이란?

🌟 부모 class로부터 매소드와 변수를 상속받는 것

 

👉 코드의 재사용성 때문에 매우 중요하다

 

b. 상속의 기본 형태

⏳ 예제

#include <iostream>
using namespace std;

class Player {
public:
	void Move() { }
	void Die() { }
	
public:
	int _hp;
	int _attack;
	int _defence; 
};

class Mage : public Player {

public : 
	int _mp;
};

int main()
{
	Mage m1;
	m1._hp = 10;	//Player의 hp를 상속받아 사용
}

 

🎨 도식화

 

 

c. Is-A vs Has-A

🌟 상속 구조인지 아닌지 판단할 때 사용하기 좋다 (Is-A 구조가 상속구조)

 

⏳ 예제

class Night : public Player	//Player는 Night 이다 가 성립하기에 상속구조 OK

class Night : public Inventory //Night는 Inventory다가 성립 X 따라서 상속 구조가 X

 

👉 이 구조를 잘 생각해서 코드를 작성해야 함

 

d. 포인터와 상속

⏳ 예제 : 플레이어가 전투하는 경우

void Fight(Player* p1, Player* p2) {
	p1->_attack -= p2->_defence;
}

int main()
{
	Knight k1;
	Mage m1;
	
	Fight(&k1, &m1);	//형변환이 이루어짐
}

 

위와 같이 플레이어가 전투하는 경우를 예를 들 수 있음

기사와 법사가 싸우는 경우 등 모든 직업의 경우의 수를 따지고 함수를 작성하게 되면 너무 비효율적

 

👉 따라서 플레이어의 클래스를 주소값으로 받아 형변환 하고 안에 함수를 작성함

 

💥 대신 아래와 같이 법사와 기사의 고유 멤버 변수는 사용할 수 없음

 

Player의 멤버변수로만 생성할 수 있음

 

💥 플레이어는 기사입니까?


이런 코드는 생기지 않음 (애초에 오류)
메모리 구조를 잘 생각하면서 인스턴스를 생성해야 함!!

 

e. 상속 계층의 종류 

플레이어가 전투하는 경우 이외에도

아래와 같이 다양한 상속 관계에서 포인터를 활용한 함수를 작성할 수 있음

대표적인 클래스의 상속구조는 아래와 같음! (참조해서 코드를 작성하도록 하자)

 

 

f. 상속 접근 지정자

다음 세대한테 부모님의 유산을 어떻게 물려줄지 결정

 

1) public : 모두 물려줌

 

2) protected : 내 상속을 보호받는 형태로 물려줌 (public -> protected)

 

3) private : 나까지만 상속 (public, protected -> private)

 

⏳ 예제 : 상속 접근 지정자

class Car {
public:
	void Move() {}

protected:
	void Dance() {}

private:
	void Drive() {}

//멤버 변수도 동일하게 멤버 제어 지시자 사용 가능
public:
	int _hp;	
	int _attack;
	int _defence; 
};

class SuperCar : private Car {
public:
	int Test() {
		Move();
		Dance();
	}
};

class UltraCar : private SuperCar {
public:
	int Test() {
		Move();		//에러 
		Dance();	//에러 
	}
};

 

📝 예제 설명

 

SuperCar에서 Car에 대한 상속을 private으로 받아

SuperCar를 상속하는 UltraCar는 더이상 상속을 사용할 수 없음

 

📌 사용하는 일은 거의 없으니 알아두기만 하자!

 

g. 다중 상속 (인터페이스)

여러가지 문제 때문에 아래와 같이 여러개의 상속을 하지는 않음

 

문법적으로 오류는 X but 사용하지 않음

 

그대신 '인터페이스'를 이용해 다중 상속을 지원

똑같이 class를 이용하지만 클래스 명 앞에 I를 붙이고, 가상 함수로 생성해 메소드의 오류를 방지함

 

이렇게 사용하게 되면
나중에 이렇게 응용가능함

 

 

2. 은닉성 (data hiding)

a. 은닉성이란?

캡슐화(encapsulation) 이라고도 함

 

🌟 클래스를 정의할 때 데이터와 해당 데이터를 조작하는 함수(메서드)를 하나의 단위로 묶어서 외부로부터 숨기는 개념

 

데이터를 보호하고 클래스의 내부 구현을 감춤으로써 객체의 상태를 안전하게 유지하고 객체 간의 상호 작용을 제어하는 데 도움

 

ex) 자동차로 예를 들면 엔진, 전기선 등..

 

은닉성을 구현하는 주요 기능은 접근 제어 지시자와 Getter / Setter 메서드가 있음

 

a. 접근 제어 지시자 : public

공개적, 어떤 부분에서든 멤버에 접근 가능

 

b. 접근 제어 지시자 : private

개인적, 클래스 내부에서만 접근 가능, 외부에서 직접 접근 X

 

private일 경우 함수 첫글자를 소문자로 하는 경우가 있음

 

c. 접근 제어 지시자 : protected

클래스 내부 및 파생 클래스에서 접근 가능, 외부에서 직접 접근 X

 

👉 즉, public과 private의 중간. 우리 class만 이용 가능하다!!

 

⏳ 예제 : 접근 제어 지시자 사용

class Car {
public:
	void Move() {}

protected:
	void Dance() {}

private:
	void Drive() {}

//멤버 변수도 동일하게 멤버 제어 지시자 사용 가능
public:
	int _hp;	
	int _attack;
	int _defence; 
};

class SuperCar : public Car {
public:
	int Test(){
		Move();
		Dance();
		Drive();	//private이라 접근 X 
	}
};

 

d. Setter / Getter

멤버 변수와 함수 모두에 접근하고 조작할 수 있는 접근 제어 지시자와는 다르게

🌟 오로지 멤버 변수만 접근하고 조작하기 위해 public 메서드 사용

 

  • Getter : 값의 설정 / Setter : 값의 제어

 

🧡 멤버 변수를 직접 수정하지 않는 이유

만일 v1.hp += 10이라고 직접 수정을 하게 되면

1) 코드가 방대해 졌을 때 코드의 흐름을 알기 어렵고
2) hp값이 수정되었을 때 일어날 수 있는 이벤트를 한꺼번에 조작하기 어렵다

ex) hp값이 1000 이상일 때 일어날 수 있는 일 동시 조작

 

 

3. 다형성 (polymorphism)

a. 다형성이란?

🌟 다양한 객체가 동일한 인터페이스를 공유하면서 다른 구현을 가질 수 있는 능력

 

  • 코드의 재사용성과 유지 보수성을 높이는 데 도움
  • 프로그램의 유연성을 향상
  • 객체 지향 프로그래밍의 핵심 원칙 중 하나인 "인터페이스에 의한 프로그래밍"을 실현하는 데 도움

 

다형성은 가상함수와 오버로딩 두 가지 방법으로 구현

 

b. 가상 함수(Virtual Functions)

🌟 기본 클래스(부모 클래스)에서 선언되고 파생 클래스(자식 클래스)에서 재정의(오버라이딩)될 수 있는 함수

 

  • 가상 함수를 사용하면 기본 클래스의 포인터 또는 참조를 통해 파생 클래스의 객체에 접근할 때 실제로 호출되는 함수가 파생 클래스에 정의된 함수로 결정
  • 다형성을 이용하여 동일한 인터페이스를 가진 객체들이 다른 동작을 할 수 있게함

 

⏳ 예제 : 가상 함수 (오버라이딩)

#include <iostream>
using namespace std;

class Shape {
public:
    virtual void draw() {
        // 기본 도형 그리기 로직
        cout << "Shape" << endl;
    }
};

class Circle : public Shape {
public:
    virtual void draw() override {
        // 원 그리기 로직
        cout << "Circle" << endl;
    }
};

class Rectangle : public Shape {
public:
    virtual void draw() override {
        // 사각형 그리기 로직
        cout << "Rectangle" << endl;
    }
};

int main()
{
    Shape* shape1 = new Circle();
    Shape* shape2 = new Rectangle();

    shape1->draw(); // Circle 클래스의 draw()가 호출됨
    shape2->draw(); // Rectangle 클래스의 draw()가 호출됨

    delete shape1;
    delete shape2;

    return 0;
}

✨ 실행결과

Circle
Rectangle

 

💙 override

함수의 상속 여부를 알기 위해 사용하는 키워드 (있으면 상속받은 것)

 

1) 정적 바인딩(static binding)

 

🌟 컴파일 시점에 결정. 일반 함수가 이용하는 방식

 

위 예제에서 아래 코드를 아래와 같이 수정한다면

virtual void draw() ➡ void draw()

 

정적 바인딩의 결과에 따라 Circle과 Rec으로 생성했음에도 불구하고 

최종적으로 변한 Shape의 값으로 단순하게 생성됨

 

Shape의 draw가 호출됨

 

2) 동적 바인딩 

 

🌟 런타임 (실행 시점) 에 결정. 가상 함수가 이용하는 방식

👉 즉 virtual 키워드를 붙이면 실질적으로 어떤 타입을 만들었는지 따라서 원본 대상의 함수를 호출함 

 

🎨 도식화

주소값을 타고 가야하니 속도가 조금 느려지긴 함
위 테이블을 vft라고 함

 

💙 소멸자도 무조건 Virtual!

만약 자식 클래스가 부모 클래스로 형 변환 후 소멸하게 되어 소멸자를 불러오는 경우
자식 클래스에 남아있던 멤버 변수의 데이터가 남아 메모리 누수가 발생하게 된다

💥 따라서 각 클래스의 소멸자를 만들어주기 위해 Virtual을 사용해야 함!!

 

c. 함수 오버로딩(Function Overloading)

🌟 동일한 이름의 함수가 다른 매개변수 목록을 가질 수 있도록 허용 = 함수 이름의 재사용

 

  • 호출 할 때 인수의 유형 및 개수에 따라 호출 함수가 결정
  • 이를 통해 다른 시그니처를 가진 함수를 하나의 이름으로 그룹화 해 사용할 수 있으므로 다형성의 한 형태임

 

⏳ 예제

void print(int value) {
    std::cout << "정수: " << value << std::endl;
}

void print(double value) {
    std::cout << "실수: " << value << std::endl;
}

int main() {
    print(5);      // int를 인수로 받는 함수가 호출됨
    print(3.14);   // double을 인수로 받는 함수가 호출됨

    return 0;
}

 

d. 순수 가상 함수를 사용하는 추상 클래스

사실 위 예제에서 살펴보듯, Shape 클래스나 Player 클래스처럼 독단적으로 사용할 일이 없는 부모 클래스가 있음 

🌟 이런 부모 클래스의 인스턴스 생성을 막기 위해 추상 클래스를 사용함 (추상클래스는 순수 가상함수로 생성)

 

  • 자식 클래스에서 가상 함수를 꼭 구현해줘야 함
  • 💥 가상 클래스를 선언해주면 부모클래스 인스턴스를 생성할 수 없음! 

 

📘 문법

class 부모클래스 {
public:
    	virtual void 메소드() = 0; //순수 가상 함수 ➡ 이거사용
        virtual void 메소드() abstract; //옛날버전
}

 

 

 


출처 : https://www.inflearn.com/course/%EA%B2%8C%EC%9E%84-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-%EC%9E%85%EB%AC%B8-%EC%98%AC%EC%9D%B8%EC%9B%90-rookiss#curriculum

 

[게임 프로그래머 입문 올인원] C++ & 자료구조/알고리즘 & STL & 게임 수학 & Windows API & 게임 서버 -

어디부터 시작할지 막막한 게임 프로그래밍 입문자를 위한 All-In-One 커리큘럼입니다. C++, 자료구조/알고리즘, STL, 게임 수학, Windows API, 게임 서버 입문으로 이어지는 알찬 커리큘럼으로 게임 프

www.inflearn.com