[C++] 상속, 다형성

haeryong·2023년 5월 3일
0

객체지향 언어의 특징

  • 캡슐화(은닉성)
  • 상속
  • 다형성
  • 추상화

상속

#include <iostream>

using namespace std;

class Parent
{
public:
    Parent()
        : i_(0)
    {}

    ~Parent()
    {}

    void setInt(int i)
    {
        i_ = i;
    }

    void print()
    {
        cout << "Parent" << endl;
    }

protected:
    int i_;
private:
};

class Child : public Parent
{
public:
    Child()
        : f_(0.f)
    {}
	
    ~Child()
    {}
    
    void setFloat(float f)
    {
        f_ = f;
        i_ = static_cast<int>(f);
    }

    void print()
    {
        cout << "Child" << endl;
    }
    
    

private:
    float f_;
};

int main()
{
    Child child;
    child.setFloat(10.0f);
    child.setInt(3);
	
    // 오버라이딩
    child.print();
    child.Parent::print();

    return 0;
}

protected

  • 자식 클래스에서는 접근이 가능하지만 이외에는 불가능함.

생성자

  • 이니셜라이저 부분에서 부모 클래스의 멤버변수를 초기화할 수 없음.
  • 자식 클래스 객체를 생성하면 Child 클래스 생성자가 먼저 생성된다.
  • Child 클래스 생성자의 이니셜라이저 맨 앞부분에는 Parent 클래스의 생성자를 호출하는 부분이 생략되어 있다.
  • 만약 Parent 클래스의 default 생성자가 아닌 특정 생성자를 사용하고 싶으면 Child 클래스 생성자의 이니셜라이저에 부모 클래스 생성자를 명시해야한다.

오버라이딩

오버로딩 : 함수의 이름은 같지만 인자가 달라서 컴파일러에서 구분 가능.

오버라이딩 : 부모, 자식 양쪽에 같은 이름의 함수가 구현되어 있으면 자식 클래스의 함수가(오버라이딩 되어) 호출된다.

소멸자

  • 호출 + 실행 모두 다 자식 클래스 소멸자를 우선한다.
  • 순서 : 자식소멸자 호출, 실행 + 부모소멸자 호출, 실행

다형성

#include <iostream>

using namespace std;

class Parent
{
public:
    Parent()
        : i_(0)
    {}

    virtual ~Parent()
    {}

    void setInt(int i)
    {
        i_ = i;
    }

    virtual void print()
    {
        cout << "Parent" << endl;
    }

protected:
    int i_;
private:
};

class Child : public Parent
{
public:
    Child()
        : f_(0.f)
    {}

    void setFloat(float f)
    {
        f_ = f;
        i_ = static_cast<int>(f);
    }

    void print()
    {
        cout << "Child" << endl;
    }

    void newFunc()
    {

    }

private:
    float f_;
};

int main()
{
    Parent parent;
    Child child;

    //Parent* parent_ptr = &parent; // 가능
    //Child* child_ptr = &child; // 가능

    // Parent* parent_ptr = &child; // 가능
    // Child* child_ptr = &parent; // 불가능
    
    Parent* parent_ptr = &parent;
    Parent* child_ptr = &child;

    parent_ptr->print(); // parent가 출력됨
    child_ptr->print(); // parent가 출력됨. 가상함수 virtual을 사용해야함.
       
    // 다운캐스팅
    Child* down_casting = dynamic_cast<Child*>(child_ptr);
    if (nullptr != down_casting)
    {
        down_casting->newFunc();
    }

    return 0;
}

포인터

  • 포인터의 크기는 모두 같지만 포인트의 종류에 따라 역참조했을 때 결과가 다름.
  • child 포인터로 parent 객체를 가리키면 메모리 상으로 parent 객체 뒤에 child 객체가 있을 것으로(실제론 없음) 예상되기 때문에 컴파일러 오류.
  • parent 포인터로 child 객체를 가리키는 것은 실제로 해당 주소에 parent 객체가 있으므로 문제가 없다.

가상함수 virtual

  • parent의 자식클래스들은 모두 부모 포인터 타입으로 포인터를 생성할 수 있다.
  • 다만, 포인터가 가리키는 실제 객체가 무엇인지 알 수 없다. 따라서 오버라이딩이 적용되지 않고, parent 클래스의 함수가 호출된다.
  • 부모 클래스의 함수에 virtual 키워드를 사용하면 해결된다.

다운 캐스팅

  • 자식 클래스에만 선언된 함수를 호출하고 싶을 때 자식 포인터 타입으로 일시적으로 캐스팅(dynamic cast)해 호출한다.

스마트 포인터 형식

#include <memory>


int main()
{
    shared_ptr<Parent> child_ptr = make_shared<Child>();
    child_ptr->print(); // virtual에 의해 child 출력.

    shared_ptr<Child> down_casting = dynamic_pointer_cast<Child>(child_ptr);
    down_casting->newFunc();

    return 0;
}

0개의 댓글