[11] Inheritance

gyeolse·2021년 12월 16일
0

C++

목록 보기
3/3
post-thumbnail

Inheritance : is-a relationship

11.2 상속의 기본

Studnet is a Person
Teacher is a Person
-> Person 으로 일반화 시킬 수 있으므로, Person Class 를 새로 만들어주자.

Child Class 에서 Mother Class 를 include 하기 때문에, Mother Class 에서 선언을 해주면 굳이 Child Class 에서 선언할 필요 없음. 당연한 이야기

클래스는 별도의 헤더 파일을 만들어서 빼는 것이 좋다.

#include <bits/stdc++.h> //비추천. 편의상 작성
using namespace std; //비추천. 편의상 작성

//Person Class
class Person {
private:
    std::string m_name;
public:
    Person(const std::string& name_in = "No name") : m_name(name_in) {}

    void setName(const string& name_in) {
        //의도적인 막음. 
        m_name = name_in; //Person 에서 public 변수가 아니니까 당연함. 
    }

	// const 선언 이유. Child 클래스에서 param 으로 받아오는데 param에서 const로 불러오고 있음. 
    string getName() const {
        return m_name;
    }
};

//Student Class
class Student : public Person {
private:
    int m_intel;
public:
    // 생성자에서 initialize 할 떄 에러가 남. m_name 은 Person 의 member variable. 
    // Student에서 Person 의 constructor를 초기화하는식으로 해야함
    Student(const string& name_in = "no name", const int& intel_in = 0)
        // : m_name(name_in), m_intel(intel_in) {}
        : Person(name_in), m_intel(intel_in) {} //Studnet 생성자를 통해서  Person 생성자를 호출

    // void setName(const string& name_in) {
    //     //의도적인 막음. Person의 m_name 을 자식에서 접근하지 않도록
    //     m_name = name_in; //Person 에서 public 변수가 아니니까 당연함. 
    // }

    void setIntel(const int& intel_in) {
        m_intel = intel_in;
    }

    int getIntel() const {
        return m_intel;
    }

    friend ostream& operator << (ostream& out, const Student& student) {

        // getName이 mother. Stduent 을 const로 params로 받아오므로, getName() 뒤에 const 선언
        out << student.getName() << " " << student.getIntel();
        // out << student.m_name << " " << student.m_intel;
        return out;
    }

    void study() {
        cout << getName() << " is studying" << endl;
    }
};

//Teacher Class
class Teacher : public Person {
private:
public:
    Teacher(const string& name_in = "no name")
        : Person(name_in) {
    }

    friend ostream& operator << (ostream& out, const Teacher& teacher) {
        out << teacher.getName(); //직접 접근 불가능
        return out;
    }

    void teach() {
        cout << getName() << " is teaching" << endl;
    }
};

Child 클래스인 Teacher, Student에서 name을 초기화하고자 할 때, parent 클래스인 Person 클래스의 생성자를 직접 호출하는 식으로 초기화를 진행해준다. 이것에 대한 설명은 다음 Chapter에서 더 자세하게 다룰 것. (생성자 순서 때문 )

    Student(const string& name_in = "no name", const int& intel_in = 0)
        : Person(name_in), m_intel(intel_in) {}

11.3 Derived Class 생성 순서

생성 순서

항상 Mother class 생성 후, Child class가 생성된다.

11.2 에서 살펴봤듯이, initializer 에서 mother class에 있는 멤버를 초기화 하는 것은 불가능 했다. 그 이유는, Child class의 initializer가 진행될 시점에서 Mother 클래스의 생성자가 호출되기 때문이다. 즉, 우리 눈에

Child() : m_d(1.0) {}

라고 보이는 것이 사실은

Child() : Mother(), m_d(1.0) {}

인 것이다. 생성자의 {} 내에서 Mother class의 멤버 변수를 호출하는 것은 됐지만, initializer 에서 호출하는 것은 안되는 이유이다.

클래스를 여러번 상속하는 경우

class A {
public:
    A() { cout << " A Constructor " << endl;}
};

class B : public A {
public:
    B() { cout << " B Constructor " << endl;}
};

class C : public B {
public:
    C() { cout << " C Constructor " << endl;}
};
A Constructor 
B Constructor
C Constructor

이 경우, Constructor는 당연히 A 먼저 생성되고, 차례대로 B, C 생성된다. DestructorConstructor의 역순이다.

11.4 Derived Class들의 생성과 초기화

Child class 가 생성될 때, Child 변수 + Mother 에 선언된 것 만큼 메모리 사이즈를 할당받는다.

11.5 상속과 접근 지정자

Base Class 가 다음과 같이 있다고 예시를 들어보자.

class Base {
public:
    int m_public;

// protected 는 child 만 접근 가능    
protected:
    int m_protected;

private:
    int m_private;
};

class Derived : private Base 인 경우

Derived class 내에서 Base class의 public, private 변수에 접근하는 것은 가능하지만, GrandChild 클래스, 즉 Derived Class 를 상속받는 클래스에서는 Base의 모든 변수 접근이 불가능하다.

class Derived : private Base {
public:
    int d_int;
    Derived() {
        m_public = 123;
        m_protected = 321;       
    }
};

class GrandChild : public Derived {
public:
    GrandChild() {
        d_int = 123;
        // Derived::m_public = 123; 불가 
        // Derived::m_protected = 123 ; 불가
    }
};

class Derived : protected Base 인 경우

클래스 내부에서는 Base 클래스의 public, protected 변수까지 접근이 가능하지만, 외부 (main) 의 경우, 접근이 모두 불가능하다.

11.6 유도된 클래스에 새로운 기능 추가

Skip

11.7 상속받은 함수를 오버라이딩 하기

class Base {
private:
    int m_val;
public:
    Base(int val_) : m_val(val_) {}
    void print() {
        cout << "I'm base" << endl;
    }
    // output operator overrloading
    friend ostream& operator << (ostream& out, const Base& b) {
        cout << "this is base output" << endl;
        return out;
    }
};

class Derived : public Base {
private:
    double m_d;
public:
    Derived(int val_) : Base(val_) {}

    // override 하고 싶을 때. 
    void print() {
        cout << "I'm Derived" << endl;
    }

    // output operator overrloading
    friend ostream& operator << (ostream& out, const Derived& b) {
        //base operator 를 불러오고 싶을 때
        //이게 왜 되냐? 메모리가 사이즈가 있으면,Base + Derived니까 
        //Base에 대한 내용을 Derived가 갖고 있기 때문에 가능함! 
        cout << static_cast<Base>(b);
        cout << "this is derived output" << endl;
        return out;
    }
};

int main() {
    Base base(5);
    // base.print();
    cout << base;
    Derived derived(7);
    // derived.print();
    cout << derived;
}

11.8 상속받은 함수를 감추기

상속 받은 변수를 클래스 내부에서 public으로 사용하는 것이 가능하다

    // m_i 가 Derived 안에서 public이 되고, 외부에서 변경 가능함. 
    using Base::m_i;

반대로, 상속받은 함수를 사용하지 못하게 막을 수도 있다.

//방법 1. private 선언 후, 작성
private:
    using Base::print;

//방법 2. delete
private:
    void print() = delete;

11.9 다중 상속

2개 이상의 클래스를 상속받는 것을 말한다.

class A 가 class B, C 를 상속받은 상황인데, B와 C에 모두 getID() 함수가 있고, getID 함수를 호출해야 할 경우,

A a("test)";

a.B::getID();
a.C::getID(); 

이런 식으로 호출하면 된다.

다음과 같은 상황일 경우를 다이아몬드 상속이라고 부른다. 다중 상속을 잘못 사용하면 다음과 같은 문제가 생길 수 있다.

문제점에 대한 해결 방법은 다형성 통해서 살펴보자.

0개의 댓글

관련 채용 정보