취미/개발서적

전문가를 위한 C++ 4판 CHAPTER 5

게임 개발 2024. 10. 10. 07:06

 

4챕터 주요 내용 요약

 
 코드 재사용 전략에서 중요하게 볼 것은 기능과 제약사항을 파학하는 것이다.
이를 위해서는 코드에 대한 문서와 공개된 인터페이스 또는 API부터 살펴보는 것이 좋다.
 
라이브러리를 선저할 때 주의해야할 사항
 

  • 멀티스레드 프로그램에서 사용해도 안전한지 파악한다.
  • 라이브러리를 사용하는 코드에 특별히 컴파일러에 설정해야 할 사항이 있는지,
  • 그렇다면 프로젝트 전체에 적용해도 문제가 없는지 확인한다.
  • 사용할 라이브러리나 프레임워크에서 의존하는 라이브러리가 있는지 확인한다.

 
추가적으로 라이브러리에 따라 다음과 같이 좀 더 구체적인 특성을 확인해아 할 수 있다.
 

  • 초기화나 종료 시 반드시 수행할 작업이 있는지 확인
  • 클래스를 상속할 때 어떤 생성자를 호출해야 하고,
  • 어떤 가상 메서드를 오버라이드해야 하는지 파악
  • 함수가 메모리 포인터를 리턴할 때 그 메모리를 해제하는 작업을
  • 호출한 코드와 라이브러리 둘 중 누가 책임지는지 파악.
  • (라이브러리에서 책임진다면 메모리 해제 시점 파악하기.
  • 라이브러리에서 할당한 메모리를 관리하는 데
  • 스마트 포인터를 사용할 수 있는지 여부도 반드시 파악하기)
  • 함수 리턴 값이나 레퍼런스 모두 파악하기
  • 함수 예외처리 파악하기
  • 라이브러리 호출할 때 어떤 에러 조건을 확인해야 하는지,
  • 어떤 상황을 가정하는지 파악하기.
  • (그냥 프로그램을 종료시키는 라이브러리 피하기)

 

5챕터 시작

 
클래스와 객체에 대한 숙련도 향상
 

  • 메서드와 데이터 멤버가 있는 클래스를 작성하는 법
  • 매서드와 데이터 멤버에 대한 접근을 제어하는 방법
  • 스택과 힙에서 객체를 사용하는 방법
  • 객체의 생명 주기가 무엇인가
  • 객체가 생성될 때 실행되는 코드를 작성하는 방법
  • 객체를 복사하거나 할당하는 코드를 작성하는 방법

 
5장에서는 C++ 프로그래밍에 대한 객체 지향 접근 방식을 설명한다.
클래스와 오브젝트(객체)에 대한 구체적인 설계 전략을 제시하고,
클래스 정의 작성, 함수 정의 외에도 컴포넌트나, 속성,
컴파일러 생성 , 초기화, 복사 생성자, 기본 생성자, 파괴자(소멸자) 및 기본 연산자 등을 다룬다.
 
클래스와 객체는 조금 다른데,
클래스가 인스턴스화 된 것이 객체라고 볼 수 있다.
 
객체지향도 철학이 있다.
이를 OOP라고 부른다.
OOP에서는 프로그램을 작업 단위가 아니라
실제 대상에 대한 모델 단위로 구성한다.
이 말을 처음 들으면 다소 추상적일 수 있지만,
물리적인 대상을 클래스, 컴포넌트, 속성, 동작 관점에서 분석하면 구체적으로 와 닿게 된다.
 
클래스
 
클래스란 개념을 적용하면 구체적인 대상과 그 대상에 대한 정의를 구분할 수 있다.
모든 객체는 특정 클래스에 속한다.
객체는 어떤 클래스에 속하는 구체적인 예(인스턴스)라고 볼 수 있다.
즉 이 말은 같은 클래스에 속한 객체끼리도 나름의 구분이 되는 특징이 있다는 것이다.
 
컴포넌트
 
비행기는 현실에 존재하는 복잡한 작은 부품(컴포넌트)로 구성되어 있다.
이는 컴포넌트의 개념과 비슷하다.
절차형 프로그래밍에서는 복잡한 작업을 절차 단위로 쪼갠다면,
OOP에서는 객체를 작은 컴포넌트 단위로 나누는 것이 중요하다.
 
속성
 
객체는 속성(property)로 구분한다.
클래스의 속성 값은 그 클래스에 속한 모든 객체에서 똑같지만,
객체 속성 값은 그 클래스에 속한 객체마다 다를 수 있다.
 
속성은 객체의 특성을 표시한다.
다시 말해 속성으로 객체끼리 구분할 수 있다.
 
동작
 
동작은 객체가 직접 하거나 그 객체로 할 수 있는 일을 표현한다.
OOP에서는 어떤 기능을 수행하는 코드를 프로시저가 아닌 클래스 단위로 묶는다.
클래스가 여러 가지 동작을 수행하고,
서로 상호 작용하는 방식을 정의함으로써
데이터를 조작하는 코드를 훨씬 다양하게 제공할 수 있다.
이러한 클래스의 동작은 메서드로 구현한다.
 
 

클래스 관계

 
has-a 관계(한 클래스가 다른 클래스의 일부인 관계)와
is-a관계(상속관계)가 있다.
 
is-a는 OOP의 핵심 개념이다.
파생, 서브클래싱, 확장, 상속등으로 다양하게 표현된다.
 
is-a 관계는 베이스 클래스로 묶어서 다른 클래스가 확장할 수 있게 만들어야한다.
그래야 공통적인 부분에서 변경사항이 생겼을 때,
상위 클래스만 고쳐도 하위 클래스에 수정 사항을 똑같이 반영할 수 있기 때문이다.
 
 

상속 기법

 
다른 클래스를 상속한 파생 클래스를 부모 클래스와 구분하는 몇 가지 방법이 있다.
파생 클래스는 이러한 기법들 중 한 개 혹은 여러 개를 조합해서 만든다.
이러한 관계를 'A는 일종의 B로서...라는 특성이 있다'라고 표현한다.
 

기능 추가

파생 클래스는 기능을 더 추가해서 부모 클래스를 확장할 수 있다.
 

기능 변경

파생 클래스는 부모 클래스가 가진 메서드를 변경하거나 무시(override)할 수 있다.
 
예를 들어 동물은 대부분 걸어서 이동하므로 Animal 클래스에 move()란 메서드를 정의한다.
그렇다 할지라도 캥거루도 일종의 동물이지만
걷지 않고 뛰어서 이동한다.
이때 파생 클래스인 캥거루 클래스는
베이스 클래스인 Animal 클래스에 있는
모든 속성과 메서드는 그대로 유지하고 move() 메서드의 구현 방식만 변경하면 된다.
 
물론 상위 클래스에 있는 속성과 메서드를 바꿔야 한다는 것은
상위 클래스가 추상 클래스가 아닌 이상 애초에 설계가 잘못된 것이다.
 

속성 추가

베이스 클래스를 상속한 파생 클래스는 새로운 속성을 추가할 수도 있다.
 

속성 변경

C++은 메서드를 오버라이드하는 것처럼
속성도 오버라이드할 수 있다.
하지만 이렇게 하는 것은 바람직하지 않을 때가 많다
 

다형성

 
다형성이란 일정한 속성과 메서드를 표준으로 정해두면
그 형식에 맞는 객체라면 어느 것이든 서로 바꿔서 적용할 수 있다는 개념이다.
클래스 정의는 객체와 그 객체를 다루는 코드가 서로 맺는 계약과 같다.
 
다형성은 OOP에서 가장 멋진 부분이다.
상속이 제공하는 장점을 제대로 살리기 때문이다.
예를 들어
모든 동물은 Animal 클래스에 속하기 떄문에
각자 이동이란 동작을 수행할 수 있다.
어떤 동물은 이동하는 동작에 대한 move 메서드를 오버라이드했을 수도 있는데,
이때 바로 다형성의 진가가 발휘된다.
구체적인 동물의 종류를 따질 필요 없이 move 메서드만 실행하는 방식으로
모든 동물을 이동시킬 수 있다.
 
has-a 관계와 is-a 관계 구분하기
 
현실에서 객체에 대한 has-a와 is-a 관계는 UML로 표현하여 대조해보면 알기 쉽다.
그 외에도 LSP(리스코프 치환 원칙 =
동작을 바꾸지 않고도 베이스 클래스 대신 파생 클래스를 사용할 수 있어야 한다)을 적용하면
is-a 관계와 has-a 관계를 쉽게 구분할 수 있다.
 
nat-a 관계
 
두 클래스 관계 중 어느 것이 적합한지 따지기 전에
그런 관계가 성립할 수 있는지부터 살펴봐야 한다.
OOP에 대한 의욕이 넘친 나머지 불필요한 클래스 및 
파생 클래스 관계를 쏟아내지 않도록 주의하자.
 
 
다중 상속 유의하기
 
다중 상속이 꼭 필요할 때가 있긴 하지만 단점도 많기 때문에
사용할 때 각별히 주의해야 한다.
다중 상속을 싫어하는 개발자도 많다.
C++에서는 다중 상속을 직접 지원하지만 
자바는 인터페이스와 추상 베이스 클래스에 대해서만
다중 상속을 지원하고 일반 클래스에 대해서는 지원하지 않는다.
다중 상속을 반대하는 이유는 다음과 같이 여러 가지가 있다.
 
 
첫째, 다중 상속 관계는 시각적으로 표현하기 복잡하다.
다중 상속 관계를 표현하는 선분이 엇갈려서 굉장히 간단한 클래스임에도 불구하고
상당히 복잡해보이게 된다.
본래 클래스 계층은 코드에 반영된 클래스 관계를
프로그래머가 쉽게 이해할 수 있어야 한다.
그런데 다중 상속으로 인해 클래스의 부모가 여러 개라서 
각 클래스의 관계를 명확히 파악하기 힘들다.
 
둘째, 다중 상속 때문에 구조의 명확성이 깨질 수 있다.
 
셋째, 다중 상속은 구현하기 힘들다.
예를 들어 이름은 같은데 동작은 다른 함수가 구현되어 있다거나
두 베이스 클래스를 하나의 공통된 베이스 클래스의 파생클래스로 만들어야하는 경우
구현이 복잡해진다.
 
프로젝트 설계에 대한 결정 권한이 있다면
다중 상속을 피하도록 얼마든지 계층을 재구성할 수 있다.
 
믹스인 클래스
 
일종의 공유 관계의 클래스라고 볼 수 있다.
믹스인 클래스는 is-a 관계를 완전히 구현하지 않고도
기능을 추가할 때 사용한다.