[필기테스트 대비] 내가 보기 위한 C++ 정리 2 (Overloading, C++ Style cast)

2024. 12. 2. 19:23Other/C++

Overloading

Overview

  • class Derived : public Base
    • public이 아닌 그 이상의 접근 제어자로 상속할 경우 해당 접근 제어자 이상의 제어자만 Derived에서 사용 가능
  • Derived(): Base(),s(str)

생성자 호출 순서

  1. 기반 클래스의 생성자 호출
  2. 파생 클래스의 생성자 호출

함수 검색 순서

  1. 어떤 함수가 파생 클래스에 정의되어 있을 경우, 파생 클래스의 함수를 호출
  2. 없는 경우 기반 클래스에서 함수를 찾아 실행

is-a / has-a

  • is-a 관계는 "사과는 과일이다" 식으로 일반화하는 관계
    • 상속을 활용하여 정의하기에 적합
  • has-a 관계는 "자동차는 엔진을 갖고 있다" 식으로 소유하는 관계
    • Composition을 활용하여 정의하기에 적합

Cast

Explicit (Conventional) Cast

  • T t = (T)someValue;
  • 타입에 대한 체크가 없기 때문에 위험

C++ Style Cast

  • static_cast<T>(value)
  • dynamic_cast<T>(value)
  • const_cast<T>(value)
  • reinterpret_cast<T>(value)

static_cast

  • 컴파일 타임에 type-safe하게 형변환
    • 타입이 명확하게 호환되어야 함
    • 다형성이 지원되어야 할 경우 dynamic_cast를 사용해야 함 (static_cast는 unsafe할 수 있음)

dynamic_cast

  • 런타임에 타입을 확인하고 변환
  • 호환되지 않을 경우 예외가 발생하거나 nullptr을 반환
  • DerivedClass * -> BaseClass * 또는 DerivedClass & -> BaseClass & 형식의 변환이 가능
  • 가상 함수가 있는 클래스에서만 작동

const_cast

  • 상수성을 임시로 제거하는 형변환
  • 실제 객체가 const로 선언되어 있는 경우 UB 발생 가능
  • 가능하면 사용하지 않는 것이 좋음

reinterpret_cast

  • 비트 단위로 타입을 강제로 변환
  • 기존 명시적 형변환과 같은 동작

Up-casting / Down-casting

  • Base * basePtr = &derived;
  • Derived * derivedPtr = static_cast<Derived *>(basePtr);
    • 강제 다운캐스팅으로 인한 런타임 오류 발생 가능성
  • Derived * derivedPtr = dynamic_cast<Derived *>(basePtr);
    • type-safe하게 형변환 가능, 실패시 nullptr 반환

가상 함수

  • virtual void f();

동적 바인딩

  • 어떤 함수를 실행할지 런타임에 결정
  • run을 virtual로 선언했을 경우, Base * basePtr = derivedPtr; basePtr->run()의 경우 Derived의 run 실행 (가상 함수를 이용하여 동적 바인딩이 이루어지므로)
  • 그렇지 않을 경우 Base의 run 실행 (정적 바인딩이 이루어지므로)

Override 키워드

  • (virtual) void f() override;
  • 함수가 이미 기반 클래스에 정의되어 있고, 파생 클래스에서 오버라이드해야 하는 경우, override 키워드 사용하여 오버라이드 하지 않는 것을 방지 가능

virtual 소멸자

  • 소멸자를 virtual로 선언하지 않는 경우 정적 바인딩으로 인해 Base 클래스의 소멸자가 호출되어 문제가 될 수 있음

vtable

  • 가상 함수 테이블
  • 가상함수가 하나라도 존재하는 클래스에 대해 vtable 생성
    flowchart TD;
    subgraph Base
      bvtable[vptr]
    end
    subgraph v1[vtable]
      bf1[f1]
      bf2[f2]
    end
    subgraph Derived
      dvtable[vptr]
      f3
    end
    subgraph v2[vtable]
      df1[f1]
      df2[f2]
    end
    v2
    bvtable-->v1
    dvtable-->v2
    f3-->actualdf3["Derived::f3()"]
    bf1-->actualbf1["Base::f1()"]
    bf2-->actualbf2["Base::f2()"]
    df1-->actualdf1["Derived::f1()"]
    df2-->actualbf2
  • 가상함수는 vptr을 거쳐 vtable을 통해 실행되므로 오버헤드가 발생

순수 가상함수

  • virtual void f() = 0;
  • 하나 이상의 순수 가상함수를 가진 클래스는 직접 생성이 불가 : 추상 클래스
  • 추상 클래스에 대한 포인터 선언은 가능

다중 상속

  • 상속하는 순서에 따라 생성자 호출
  • 상속한 서로 다른 클래스에서 동일한 함수가 존재할 경우, ambiguos 에러 발생
  • 다이아몬드 상속으로 인해 중복 문제 발생 가능

가상 상속

class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
  • 다이아몬드 상속 문제 해결을 위해 가상 상속 후, 각각의 클래스 생성자 호출을 통해 해결 가능
  • 기본 클래스의 인스턴스가 파생 클래스에 의해 한번만 생성
  • B와 C가 A의 인스턴스를 직접 갖는 대신, vbptr(virtual base pointer)을 통해 간접적으로 참조
  • vbptr은 vbtable(virtual base table)을 가리키고, a의 유일한 오프셋에 대한 정보가 저장 됨
    • vbptr (virtual base class table pointer) 추가로 인한 오버헤드 발생