[C++] 6.2 가상함수와 다형성

Sireal·2022년 3월 10일
0
post-thumbnail

따라하자 씹어먹는 C++


is '-a'와 'has -a'

상속받을 때 사용됐던 아래 문법을 보고 해석해봅시다
class Manager : public Employee

  • Manager 은 public Employee 를 상속한다.
  • Manager 은 Employee 의 모든 기능을 포함한다.
  • Manager 은 Employee 의 모든 기능을 수행할 수 있기 때문에, Employee 라고 칭해도 무방하다.
  • 즉, 모든 Manager 은 Employee 이다.
    • Manger is Employee!!

위와 같은 관계가 'is a' 관계이다.

근데 상속에 상속을 하다보면 기능들이 자꾸 하나 둘씩 늘어갈거다.
그렇게 점점 구체화 가 이루어진다.

그렇다 보면 'is a' 관계가 아니라 'has a' 관계가 될 수도 있다.

  • 위에 예는 'Employee is a Manager' 관계 였다.
  • 자동차로 예를 들면 'Car has a Engine' 같은 관계 만들 수 도 있다.
class Car {
 private:
  Engine e;
  Brake b;  // 아마 break 아니냐고 생각하는 사람들이 있을 텐데 :)
  ....
};

그럼 예전 예제에서 main문을 고쳐서 포인터로 클래스를 받아와 보자

int main() {
	Base p;
	Derived c;

	std::cout << "===포인터 버전===" << std::endl;
	Base* p_c = &c;
	p_c->what();

	return 0;
}

  • 이.. 이게 무슨일이야
    • 분명 파생클래스를 가져왔는데 왜 기반클래스의 what()을 불러온거야?
    • 이유는 Derived is a Base
    • 이렇게 파생클래스를 기반클래스로 캐스팅하는것을 업캐스팅이라고 한다.

그럼 다운캐스팅은 가능할까?

  • 안된다!
  • Derived is a Base 만 기억하자.
  • 파생 is a 기반. C++에서는 이 규칙 하나만 정해뒀다.
    그래서 다운캐스팅은 금지되었다.
  • 머선일이 있어도 기반을 가지고 캐스팅된다.

Derived is a Base 을 기억하자.

  • 다운캐스팅을 타입캐스팅하여 강제로 사용가능하게 할 수 있지만,
    오류가 펑펑펑 터질거니 하지말라는 건 하지말자.

dynamic_cast

Derived is a Base를 깨려고 하는 친구들을 위해
사전방지용으로 dynamic_cast 를 지원한다.

Derived* p_c = dyanmic_cast<Derived*>(p_p);

  • 이렇게 쓰면 오류가 난다.
  • C++에서 말하는 규칙은 바꾸지마라 그냥

virtual 키워드

virtual 키워드를 사용해서 코드를 짜보자

#include <iostream>

class Base {

public:
	Base() { std::cout << "기반 클래스" << std::endl; }

	virtual void what() { std::cout << "기반 클래스의 what()" << std::endl; }
};
class Derived : public Base {

public:
	Derived() : Base() { std::cout << "파생 클래스" << std::endl; }

	void what() { std::cout << "파생 클래스의 what()" << std::endl; }
};

int main() {
	Base p;
	Derived c;

	Base* p_c = &c;
	Base* p_p = &p;

	std::cout << " == 실제 객체는 Base == " << std::endl;
	p_p->what();

	std::cout << " == 실제 객체는 Derived == " << std::endl;
	p_c->what();

	return 0;
}

결과는 Derived is a Base 니까 둘다 기반클래스의 what()을 들고올것으로 예상한다

  • 내 예상은 개같이 멸망했다.
  • 각자 알맞게 what()을 들고왔다.. 왜지?
class Base {

public:
	Base() { std::cout << "기반 클래스" << std::endl; }

	virtual void what() { std::cout << "기반 클래스의 what()" << std::endl; }
};
  • virtual 의 힘이다. 가상함수를 둬버려서 알맞게 함수가 호출됐다.
    • virtual function으로 오버라이딩 하려면 두함수의 꼴이 정확히 같아야함. 을 기억합시다. 아무때나 사용하는 게 아닙니다.

overide

C++11 (으악 나는 10인데)이상부터는 override 키워드를 넣을 수 있다.

  • 가상함수를 사용할때 파생클래스의 함수에서는 override라는 키워드를 넣어줄 수 있다.

virtual 함수 좀더 뜯어보기

  • 소멸자는 virtual로 처리해줘야한다.
  • 모든 함수에 virtual 로 처리해도 되지 않냐?
    • Java에서는 실제로 모든 함수의 디폴트는 virtual함수다
    • 근데 그럴경우 오버헤드(overhead)가 난다.
    • 괜히 복잡하게 하지말고 적당히 쓰자

순수 가상함수, 추상 클래스

추상클래스 : 순수 가상함수를 최소 한개 포함하고, 반드시 상속되어야하는 클래스

class Animal {
 public:
  Animal() {}
  virtual ~Animal() {}
  virtual void speak() = 0;
};
  • 순수 가상함수는 꼭 오버라이딩 해줘야한다.

다중상속

#include <iostream>

class A {
 public:
  int a;

  A() { std::cout << "A 생성자 호출" << std::endl; }
};

class B {
 public:
  int b;

  B() { std::cout << "B 생성자 호출" << std::endl; }
};

class C : public A, public B {
 public:
  int c;

  C() : A(), B() { std::cout << "C 생성자 호출" << std::endl; }
};
int main() { C c; }
  • C가 A,B를 다중상속하는 모습이 보인다.
  • 결과는 A 생성자 호출 B 생성자 호출 C 생성자 호출 이렇다
    • 호출되는 순서는 상속한 순서대로 나온다.

다중상속 주의점

class A {
 public:
  int a;
};

class B {
 public:
  int a;
};

class C : public B, public A {
 public:
  int c;
};

int main() {
  C c;
  c.a = 3;
}
  • 이와 같이 짜면, 클래스간 멤버변수가 겹치게 된다.
  • 겹치는 멤버변수를 파생클래스가 부르면 당연하게도 에러가 뜬다.(누구껀지 못찾아서)

가상 상속

virtual 상속을 받아서 해결하는 방법
사람을 예로 들자

 C/C++ 확대 축소
class Human {
  // ...
};
class HandsomeHuman : public Human {
  // ...
};
class SmartHuman : public Human {
  // ...
};
class Me : public HandsomeHuman, public SmartHuman {
  // ...
};

Human 클래스가 기반클래스가 되고, HandsomeHuman, SmartHuman이 Human의 파생함수, 그리고 Me가 HandsomeHuman, SmartHuman의 다중상속으로 파생함수가 된다.

근데 이렇게 짜면 바로 중복문제에 부딛히게된다. 그럴때 어떻게 짜느냐

class Human {
 public:
  // ...
};
class HandsomeHuman : public virtual Human {
  // ...
};
class SmartHuman : public virtual Human {
  // ...
};
class Me : public HandsomeHuman, public SmartHuman {
  // ...
};
  • Human을 virtual 로 상속받으면 깔~끔하게 해결
profile
🚄계속 앞으로🚄

0개의 댓글

관련 채용 정보