Adventure of 빠타박스
article thumbnail
728x90
728x90
SMALL

ImGui 활용한 DirectX 11 3D를 이용한 2D를 만드는 내용입니다.
본 내용은 똑같이 사용할 수 없음을 알립니다. (참고 및 공부자료로 만들어진 내용임을 밝힙니다.)
전 과정에 대해 이해를 해야 다음 이 부분에 대한 이해를 할 수 있음을 밝힙니다

이전 자료) https://ppatabox.tistory.com/50

 

커플링.... 그.. 반지 아니다.

(... 나도.. )

 

 

 

 

상속 / 결합도 / 응집도 / 커플링 

각주 참고 하단이동[각주:1]

 


객체지향의 4개는 

캡슐화 (정보은닉성과는 좀 다른의미) 

private로 숨기고

public 으로 외부에서 접근할 것만 판단한다는 것 

 

 

우리가 예를 들어 

sprite* Sprite = new ()~~~~~~~ 라는 어떤걸 만들어 놓았는데

 

Sprite의 안에 있는 함수 자체가 변하지 않는한 외부에서 건드릴 것은 없다. 

 

그게 왜 가능하냐?

Public으로 공개 할 것만 공개 했기 때문에 

그러니까 내부적으로 뭔가가 바뀌어도 외부에 영향을 미치진 않는다는 말

 

이게 객체지향의 정보 은닉성에 대표적인 좋은 예?

 

 


 

그러면 생각해보자 

회사에 가면 부서가 여러 파트로 나뉠 것이다. 

 

예를들어 엔진팀과 클라이언트 팀이 있다고 생각해보자 

 

엔진팀이 무얼 바꿨다 

그런데 그게 클라이언트에게 영향을 많이 미치면 

클라이언트들은 이중작업을 하게 된다. 

또 클라 쪽에서 수정되면 엔진에서 또 손을 봐야 하는 가능성이 생긴다.

이런 상황을 커플링이라고 한다. 

그런데 프로그래밍에서 커플링은 최악의 순간이라고 한다. 

 

 

원래 프로그래밍을 설계할 때 가장 좋은 설계 요소는 

1) 결합도는 낮추고 (커플링이 될 수 있는 요소는 낮춘다)

2) 응집도 비슷한 것들끼리 모아 놓는 게(가장 좋은 소프트웨어 설계요건) 

 

 

이렇게 나눠두고 

 

 

클라이언트에서 콘텐츠 담당 / 이벤트 담당/ 캐릭터 담당/ 아이템 담당/ 로컬 국제 언어 쪽 처리 담당

이렇게 많은 업무에 서로 간의 업무에 영향을 미치면 안된다. 

 

최소화해야 하는데 어쩔 수 없이 서로간의 협의를 해야 하는 경우도 있지만.

영향을 미치는 것을 최소화하는 게 서로 간 업무가 독립적이 되는 게 가장 좋은 것 

 

 

그럼 엔진도 마찬가지 일 것이다.

쉐이더 / 툴 / 그래픽스 / 코어 담당 등이 있을 것이다.

그럼 엔진팀의 그랙픽스 팀이랑. 클라에 콘텐츠 팀이랑 업무 협의는 하겠지만

업무 영향을 최소화시켜줘야 하지 않을까? 그래야 팀장도 관리가 편해진다.

 

이런 걸 수행하기 위한 것이 

말 그대로 객체 지향이다. 

 

그러면서도 관리는 또 편해야 하니까. 

일원화를 또 할 수 있어야 하지 않을까?

 

예를 들어 캐릭터의 크기를 구하는데

엔진 = length 클라  = sizeof 라고 한다면

말이 안 되는 상황이 된다. 

 

그래서 그것들을 통일시키기 위함이 상속성이다. 

그런데 상속을 많이 받으면 별로 좋지는 않다. 

언리얼은 20~30단계가 들어가긴 한다.

이처럼 언리얼은 너무나 설계가 잘되어있어서 가능한 것이다.

(수많은 프레임워크 중에 언리얼이 정말 잘되어있다) 

 

 


자 이제 상속에 대해서 생각해보자 

독립적으로 돼야 한다는 분할 개념을 가지고 

 

게임을 크게 나누면 

 

이런 식으로 분할할 수 있다. 

 

Characters = 캐릭터가 움직이거나 어떤 상호작용을 해야 하는 부분 

 

Weapons = 그다음 무기 그룹 (검, 활, 창, 등등) 

 

Items = 아이템 그룹(반지, 물약, 버프 아이템 그 외 등등) 

 

간단히 이렇게 만 있다고 해보자 

그러고 나서 캐릭터에서 분류를 해보자 

상속을 잘하려면 분류를 잘해야 한다 (그룹화)

카테고리에 원하는 것 끼리 모아 놓는 것처럼 

 

그럼 캐릭터부터 한다고 하자 

 

캐릭터 안에 -> Playable - Noneplayable 이렇게 2개가 있다하면 

Noneplayable : Monster, Boss, NPC가 있을 것이다. 

이것들이 모두 가져야 하는 요소가 있을 것이다. 

모두 가져야 할 요소는 캐릭터에 넣는 것이다. 

이를테면 HP MP등등 

그러면 모두가 HP를 가지고 있다고 간주하고 

 

그럼 Characters에다가 HP를 넣으면 상속을 이용해서 

모두가 HP를 갖게 될 것이다. 

 

그런데 이것도 잘 생각해야 하는 부분이 있다.

예를들어 NPC는 움직임이 없을 것이다. 

그런데 Character안에다 모두가 움직이는 것으로 Move를 넣어주면 

NPC 쪽은 문제가 생길 수 있을 것이다.

(물론 회피하는 방법도 있긴함 이럴때는 조합으로 사용하는 기본뼈대는 상속으로 두고 거기에 필요한 개념을 추가하는 기능 컴포넌트 패턴이라는 기능이 있다. 언리얼이 주로 이런 방법을 씀) 

 

 

컴포넌트 패턴[각주:2]

 

이것은 해당 회사의 팀장의 재량에 따라서 

상속을 쓰거나 

컴포넌트를 조합하는 개념을 쓸 수 있다 

 

어떤 시스템이냐에 따라서도 다르고 

어떤 스타일이냐에 따라서도 달라진다. 

 

그래서 상속을 먼저 쓰면서 

상속에 있는 어떤 추상화 

오버로딩(생성자 여러개 (같은이름이 여러개 정의 되는 것)) 오버라이즈 

 


이제 Game이라는 폴더를 하나 만들어서

게임 관련된 곳에 이곳에 넣자

 

Character.cpp/h를 만들자 

 

캐릭터는 무얼 가져야 할까??

1) 그려줘야 한다. (그림) Sprite 

2)화면에 어디에 출력되야 하나(그려질 위치 중심좌표) position

3)얼마만큼 크기가 있어야 하나 Scale

4)모든 애들이 이동을 가질 것이다. 

 

그래서 이걸 가지고 상속을 테스트 해보자 

 

stdafx.h에 갖춰놓고 시작을 할 것이다. 

컬러로 하면 컬러 변수명이랑 헷갈려서 4를 넣어줌 

 

이름바꿔 넣는것 (D3DXMATRIX) 

 

 

 

 

설명은 하단 

Character.h에서 

#pragma once

class Character
{
public:
	Character();
	~Character();

	void Initialize(wstring file, Vector2 position); 

	void MoveUp(); 
	void MoveDown();

	void MoveRight();
	void MoveLeft();
    
    void Update();
	void Render();

private:
	Sprite* sprite;

	Vector2 speed = Vector2(20, 20); //속도정의 

	Vector2 position;
	Vector2 scale = Vector2(50, 50);
};

 

 

Character.cpp

#include "stdafx.h"
#include "Character.h"

Character::Character()
	: sprite(NULL), position(0, 0) //Default 생성자에 초기화만 이니셜라이즈에서는 저렇게만 해도 생성자 안쓰고 내부생성자call하기때문
{

}

Character::~Character()
{

	SAFE_DELETE(sprite);
}

void Character::Initialize(wstring file, Vector2 position)
{
	assert(file.length() > 0); //파일명이 하나라도 입력이 안되면 안되니까. 항상 참인 조건이 들어가야한다. 

	sprite = new Sprite(file);
	
	this->position = position;
}

void Character::MoveUp()
{
	position.y += speed.y * Time->ElapsedTime();
}

void Character::MoveDown()
{
	position.y -= speed.y * Time->ElapsedTime();
}

void Character::MoveRight()
{
	position.x += speed.y * Time->ElapsedTime();
}

void Character::MoveLeft()
{
	position.x -= speed.x * Time->ElapsedTime();
}

void Character::Update()
{
	sprite->Position(position);
	sprite->Scale(scale);
	sprite->Color(Color4(1, 1, 1, 1));

	sprite->Update();
}

void Character::Render()
{
	sprite->Render();
}

 

initialize라는 것을 이용해서 초기화를 할 것이다. 

void Character::Initialize(wstring file, Vector2 position)
{
	assert(file.length() > 0); //파일명이 하나라도 입력이 안되면 안되니까. 항상 참인 조건이 들어가야한다. 

	sprite = new Sprite(file);
	
	this->position = position;
}

assert(file.length() > 0); assert에는 항상 참인 조건이 들어가야 pass한다 

여기서는 생성자가 아니기 때문에 initialize를 사용하지 못하므로

이런식으로 정의한다. this->position = position; 

 

Character::~Character()
{
	SAFE_DELETE(sprite);
}

끝나고 띄어주는 messagebox 

 

지금은 여기에 플레이어 쪽만 정의 하고 

플레이어 기능을 넣어놓고

플레이어를 분할하고 

몹을 나눌 것임

 

처음에는 설계하기 힘든이유가 

이걸 나눠두고 생각하면 힘들다

 

처음에 한덩어리를 만들어두고 

분할이 필요할때만 분할 하면된다. 

 

 

 

우리가 이동이나 이런것을 할때는 속도가 빠른것은 빠르게 움직이고

느린건 느리게 움직인다고 얘기 했다 

이전시간과 현재시간의 차를 곱해주면 일정시간을 고정할 수 있다고 했는데

Time->ElapsedTime(); 이라고함 

void Character::MoveUp()
{
	position.y += speed.y * Time->ElapsedTime();
}

void Character::MoveDown()
{
	position.y -= speed.y * Time->ElapsedTime();
}

void Character::MoveRight()
{
	position.x += speed.y * Time->ElapsedTime();
}

void Character::MoveLeft()
{
	position.x -= speed.x * Time->ElapsedTime();
}

 

 

void Character::Update()
{
	sprite->Position(position);
	sprite->Scale(scale);
	sprite->Color(Color4(1, 1, 1, 1));

	sprite->Update();
}

 

컬러는 어차피 바뀔때 한번만 하면 되는데 

이동은 계속 바뀔 것이다. 

그래서 업데이트 때 

 

sprite->Position(position);

다시 넣어주는 것 

 

렌더 넣어주기 

void Character::Render()
{
	sprite->Render();
}

 

 

main.cpp 업데이트 부문에 넣어주었음 

{
	if (Key->Press('W'))
		MoveUp();
	else if (Key->Press('S'))
		MoveDown();

	if (Key->Press('D'))
		MoveRight();
	else if (Key->Press('A'))
		MoveLeft();
}

전에 다룬거 보았다면 이해 가능 

각 키로 움직이는 걸 볼 수 있다. 

 

void Character::MoveUp()
{
	position.y += speed.y * Time->ElapsedTime();
}

void Character::MoveDown()
{
	position.y -= speed.y * Time->ElapsedTime();
}

void Character::MoveRight()
{
	position.x += speed.y * Time->ElapsedTime();
}

void Character::MoveLeft()
{
	position.x -= speed.x * Time->ElapsedTime();
}

뭐 아무튼 모든 플레이어가 이걸로 움직이게 될 것이다. 

그래서 나는 플레이어 클래스를 그대로 상속받아서 해보자 

 

Game / player.h/cpp 

 

캐릭터 클래스 부터 상속을 받을 것이다. 

 

class Player : public(의미가 따로 있다. private는 불가능 일반적으로 public사용) 

 

#pragma once
#include "Character.h"

class Player : public Character
{
public:
	void Move() override;
};

 

우리가 플레이어를 정의하는데 있어서 캐릭터를 상속을 받았다. 

class Player : public Character

 

그러고 위에 #include라고 했다. 

여기에 헤더.h .cpp에 둘중에 하나라도 다 들어갈 수 있다. 

예를 들어 

 

뭐 A.h / A.cpp가 있다고 하자 

만약에

B.h 을 정의 하는데  

#include "A.h" 했다

 

또 C.h 가 있고,  C.cpp 가 있다

여기서 다시 #include "B.h"를 하게 되면 

 

B헤더가 물고 있는 

A.h을 C.cpp에 같이 물고 간다. 

 

만약에 A.cpp에 #include ~를 했다하면 

C.cpp에 #include "A.h"라고 하면 

A.cpp에 #include한것은 안온다. 

 

cpp에 있는 것은 오지 않고 헤더에 있는 것만 전파된다. 

A.h에 있는 것이 B.h에 누적되고 B.h에 있는 것이 C.cpp에 누적 되면서 간다.

 

그런데 상속은 

#include "Character.h"

class Player : public Character
{

이곳에 Character.h 헤더를 써둔 것인데.

 

누군가 Player를 상속받으면 

상속받은 X는 player를 쥐고 있고 Character도 쥐고 있어야 한다. 

값의 부모부모 니까 

 

그래서 지금 정의가 상속일때 헤더에 들어간 것이다. 

(부모의 부모까지 다 쥘 수 있게, 계속 전파가 되서 오니까) 

 

 

이렇게 한번 생각해 보자 ?

왜 필요해 질까?

 

예시임 (실제로 이렇게 하진 않는다) 

 

Player.h에서 

#include "Character.h"

 class Player : Character () 클래스 플레이어 캐릭터를 물었다

 

그다음 

Monster.h에서 

#include "Player.h" 하고 

 class Monster : public Player()

//Player.h에서 

#include "Character.h"
 class Player : Character () //클래스 플레이어 캐릭터를 물었다


////////////////////////
///////////그다음 
/////////////////////////
//Monster.h에서 

#include "Player.h" 하고 
 class Monster : public Player()
 
///////////////
//main.cpp 에서  
////////////
#include "Monster.h"  //몬스터 하나만 쥐게 되면 (전파 된다)

Character* player = new Player()

//설명//
Character.h에서 Character를 Player가 쥐었고 
Player.h를 Monster.h에서 물었으니까. 
Monster.h는 이미 Character.h와 같이 가지고 있는 상태가 된다. 

그러고나서 main.cpp에서 Monster.h를 쥐면 
player도 쓸 수 있고 Character도 쓸 수 있고 Monster도 쓸 수 있게 된다. 
어차피 다 상속이니까 3개중에 하나를 다 써야한다.

 (실제로는 Character* player = new Player() 이런식으로 만든다) 

= 이렇게 쓰는 이유가 있다. 이게 virtual 함수에서 나오는데 

이걸 가상함수 라고 부른다. 

 

그러면 플레이어는 Character.h에서 가질게 없다. 

 

 

 

다 이대로 그냥 쓰면 되는 것이다 

 

플레이어는 컬러를 쓰지 않고 기본값 1을 가질 것이다. 

그래서 Initialize에 컬러 셋팅한게 있는데 빼주었다.

 

 

 

 

캐릭터.cpp에서 수정 

컬러전부 뺴주기 

함수도 빠지고 

 

플레이어는 컬러는 원색으로 해준다.

sprite->Color(Color4(1, 1, 1, 1));

 

그러고  main.cpp에서 

 

#include "Game/Player.h"

Character* player - NULL; 부분이 있는데 

 

플레이어 헤더에

캐릭터가 전파가 되어있으니까. 

main.cpp에도 Character* player  여기 player에게도 전파가 되었을 것이다. 

 


이제는 이렇게 한번 생각해보자 

캐릭터가 가지고 있는 요소를 생각해보자

나는 플레이어의 이동을 입력받을 것이고 무브를 외부에서 콜해줄 것이다

무브가 콜이되면 키보드로 다룬다

어떻게 움직일 것이냐는 move에서 

 

이렇게 하면

Character 

- Sprite

- Position

- Scale

- Move

.

그럼 몬스터도 무브가 있을 것인데

Player

- Move 

Moster

- Move (AI 행동이 달라짐) 

 

플레이어도 무브가 있고

몬스터도 무브가 있다(키보드로 움직이는게 아님) 

행동이 다른데 캐릭터 입장에서 무브를 어떻게 정의해야할까?

이럴때 사용하는게 일반가상함수와 순수가상함수를 사용한다.

 

 

 

*일반가상함수 : NPC가 있다 NPC는 움직임이 없다. 그러면 

Character안에 Move 가 virtual Move() { } 가 되어 가상함수가 된다. 

virtual Move() { } = 아무것도 지정해주지 않음

 

또한 Player 에 있는 Move는 재정의를 할 것이다 .

Monster에 있는 Move도 재정의 된다. 

 

그런데 NPC에 있는 Move를 재정의를 안할 것이다. 

NPC는 움직임이 없으니까. Move() { } 아무것도 처리 안하면 된다

이럴때 가상함수를 쓴다.

 

혹은 

공통적인 Move의 기능을 갖거나 

추가적으로 무언가 Move를 처리해야 할게 있다. 이럴때도 가상함수를 사용한다. 

 

*즉 기본적인 기능은 제공해주고 추가적으로 자기의 기능을 무언가 추가해야할때 

 

뭐 그런데 그게 아니다 할때 

자식마다 무언가 달라지고 싶다! 이동이 달라지고 싶다 할때

그럴때는 *순수가상함수를 사용한다.

그러고 대신 캐릭터는 생성 불가능하게 한다.(move가 어떻게 될지 모르니까)

이 Character가 어떻게 이동해야 될지 모르니까

Character정의 객체 생성 불가

 

순수가상함수가 하나라도 포함되어 있다면 그것을 *추상클래스 라고 불린다. 

*추상클래스 -> 상속받은 자식이 몸체를 구현하지 않았다면 그(자식)것도 추상클래스 이다. 

즉 추상클래스는 몸체가 없기 때문에 메모리 할당이 불가능하다. 

 

.프로그램이 이객체는 생성할 수 없다고 함 그렇게 강제로 해주는 것 

이것은 반드시 재정의 되어야 한다. (어디선가 최후의 자식에게 재정의 해야한다 강조를 해줘야한다)

 

물론 예를 들어 Player에서 순수가상함수로 재정의 해줬다

그런데 그 player안에 밑에 또 재정의가 필요하면 그때 또 가상함수로 재정의 한다.

한번재정의가 나올때 까지 순수가상

 

(ex : A 에서 Move = 0; 함수가 아무것도 없다고 순수를 했다 

그러고 나서 B 에 이렇게 아무것도 재정의를 안했다 그럼 추상클래스 이다>

A Move() = 0; //순수
B //추상클래스
//////////
C Move() 재정의 (어떤역할을 넣음) //밑에 D에 정의해주면 이젠 순수가상이 아니라 가상 앞에 virtual을 안붙이면 일반함수 
D Move() { }

 C 앞에 virtual을 안 붙여준다면 D 를 재정의 불가하다.

D 이러면 일반함수로 바뀐다. (virtual이 붙어야함) 

 


 Character로 가자 

Monster를 만들고 Character에서 상속받으면서 분할을 할 것이다. 

Game / Monster.cpp/h

 

Character.h에서  Move를 만들것인데

#pragma once

class Character
{
public:
	Character();
	~Character();

	void Initialize(wstring file, Vector2 position); 

	void Update();
	void Render();

public:
	virtual void Move() {}

private: //내부호출 
	void MoveUp();
	void MoveDown();

	void MoveRight();
	void MoveLeft();

private:
	Sprite* sprite;

	Vector2 speed = Vector2(20, 20);

	Vector2 position;
	Vector2 scale = Vector2(50, 50);
};
public:
	void Move() {}
///////////////  
//이것을 생각해보니까. 플레이어랑 몬스터 로 나뉘는데
//어떻게 처리해야 할지모르겠다 이렇게 두면 재정의 불가능 하다 
/////////그래서 아래처럼 

public:
	virtual void Move() {} //로 만들어주자 
///////////////
//나는 지금 어떻게 되야 할지 모르겠으니까. 너가 알아서 하세요~ 라는 의미로
//virtual을 붙여주게 된다. 
///////////////

 

Plyer.h에서 

class Player : public Character
{
public:
	void Move();//여기에 virtual을 써둘 수 도 있고 안써놓을 수도 있다. 
    //안써두면 Move는 일반함수로 바뀌고 (단. 재정의 불가)
    //virtual해주면 player의 자식에서도 재정의가 가능하다

권고[footnote 무엇을 하도록 권하는 것을 말한다. 추천(recommendation)보다 강한 의미로 쓰이지만 법적 강제력은 없다[/footnote]

 

의무사항은 아닌데 Visual Studio 권고사항으로 나는 재정의 했다며 

override를 붙여준다 . (이것은 재정의 된 함수이다 라고 붙여주는것) =그저 알아보기 쉽게 하기위함

public:
	void Move() override;

 

Player.cpp 에서 main.cpp가져온 키를 호출 

void Player::Move()
{
	if (Key->Press('W'))
		MoveUp();
	else if (Key->Press('S'))
		MoveDown();

	if (Key->Press('D'))
		MoveRight();
	else if (Key->Press('A'))
		MoveLeft();
}

여기서 보게되면 지금Character영역안에있는 moveup을 엑세스 할 수 없다고 뜬다. 

이건 부모에 접근할 수 없다는 말이다. 

 

아까 Character.h에서 Move관련된걸 private로 바꿔 주었었다

Private는 해당 클래스 내에서만 접근이 가능하다.

 

Player는 상속을 받는 거지 좌표를 쓰는게 아니다.

 

그래서 자식에게 접근을 허용해줄려면 

protected: 를 사용한다. 

 

public[각주:3]

private[각주:4]

protected[각주:5]

 

 

Monster.h에도 재정의 해주자 

#pragma once
#include "Character.h"

class Monster : public Character
{
public:
	void Move() override;

private:
	bool bReverse = false;
};

 

 Monster.cpp

#include "stdafx.h"
#include "Monster.h"

void Monster::Move()
{
	bReverse = ((int)Time->Running() % 2 == 1) ? true : false;

	if (bReverse)
		MoveRight();
	else
		MoveLeft();
}

 

main.cpp 

#include "stdafx.h"
#include "Game/Player.h"
#include "Game/Monster.h"

Player* player = NULL; //만들어진 자료형
Monster* monster = NULL; //몬스터 추가 

void InitScene()
{
	player = new Player();
	player->Initialize(L"SingleMario.png", Vector2(500, 500));

	monster = new Monster();
	monster->Initialize(L"SingleMario.png", Vector2(500, 600));
}

void DestroyScene()
{
	SAFE_DELETE(player);
	SAFE_DELETE(monster);
}

void Update()
{
	player->Move();
	player->Update();

	monster->Move();
	monster->Update();
}

void Render()
{	
	player->Render();
	monster->Render();
}

이제는 Player에 있는 Move를 호출한 것이다. 

Character에 있는 Move이면 안된다. 

이 캐릭터의 Move에는 아무런 역할이 없다. 

그래서 안움직인다. 

 

 

 

 

 

춘식이 몬스터가 지금 좌우로 왔다 갔다 하는 걸 볼 수 있다. 

지금 상속받은 애들을 재정의해서 서로다른 움직임을 만든 것이다. 

 


요약해보자 

 

Character.h에는 아무런 역할이 없어서 이동이 없었다

virtual Move() {}; 

 

player.h에서 재정의해서 Player에 맞도록 나머지는 다 공통으로 쓰고

Monster.h에서도 Move가 Move에 맞도록 재정의를 해주었다.

이게 지금까지 재정의 할 수 있는 기능인 (가상함수) virtual 추상화 한 것이다. 

 


 

Character.cpp로 가자 

소멸자 ~Character()

Character::~Character()
{
	MessageBox(NULL, L"소멸자 호출", L"소멸자", MB_OK);

	SAFE_DELETE(sprite);
}

messagebox띄우기 

메인창의 식별자들 

L"내용" , L"경고창" , messageBox ok 라는 의미);

 

2번 메세지 박스 뜨면서 내용 보여주고 있다. 

 

player.cpp에 들어가서 확인해보자 

#pragma once
#include "Character.h"

class Player : public Character
{
public:
	virtual ~Player()//가상소멸자 
	{
		MessageBox(NULL, L"Player 소멸자 호출", L"소멸자", MB_OK);
	}

public:
	void Move() override;
};

 

여기에 virtual을 안 붙여도 되는 이유가 있다. 지금 

실행해보면 virtual이 없어도 실행은 된다. 

 

이 가상소멸자는 가상화 때문에 나오는 얘기인데 

 

다음 시간에 

가상소멸자 ~/ 가상화가 될 수 있는 조건을 다뤄볼 것이다. 

가상함수테이블이라는 용어가 나오는데(면접 단골 손님) 

 

가상함수테이블의조건


 

 

 

 

 

 

 

 

 

 

참고할만한 링크)

 

결합도(Coupling), 응집도(Cohesion)

낮은 결합도, 높은 결합도응집도 소프트웨어 공학의 전통적인 이론에 따르면, 유지보수성이 높은 소프트웨어는 프로그램의 각 요소들이 결합도는 낮게, 응집도는 높게 구성되어야 한다. 결합도

lazineer.tistory.com

 

다음시간) 

 

 

 

 

 

 

 

 

 

 

 

 


각주)                                                                                                                                                                                            

  1. . [본문으로]
  2. 로직을 기능별로 컴포넌트화 하는 것으로, 기능들을 나누어 각각 독립적인 클래스로 분리하는 것, 기본뼈대는 상속으로 두고 필요한 개념을 추가하여  기능 [본문으로]
  3. 접근을 모두에게 허용하는 것 [본문으로]
  4. 접근을 자기 내부에서 접근하는 것 [본문으로]
  5. 접근을 자기 내부 + 자식에서도 가능하게 하는 것 [본문으로]
728x90
728x90
LIST

'Programming > DirectX 2D' 카테고리의 다른 글

[C++] DirectX_Animation 2부/ static 함수/변수  (0) 2021.12.03
[C++] DirectX Rect&Sprite 3편  (1) 2021.12.01
[C++] DirectX Rect&Sprite 2편  (0) 2021.11.29
profile

Adventure of 빠타박스

@PPATABOX

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!