가티있는블로그

[C++] 상속

2021. 8. 21. 16:15 | 프로그래밍/C++

공통의 특징을 모아서 클래스를 설계해서 상속구조로 나타낼 수 있다.

상속은 한 클래스가 다른 클래스에서 정의된 속성들 (데이터, 함수) 를 이어받아서 사용하는 것.

class Person
{
    std::string name;
    int age;
};

class Professor : public Person
{
    int major;    
};

class Student : public Person
{
    int id;
};

 

 

상속의 장점

코드의 중복을 막을 수 있다

상속을 통해서 기존 클래스에 새로운 특징을 추가한 새로운 타입의 설계

다형성을 활용한 객체지향 디자인 기법

 

 

UML 표기법

 

 

부모클래스: Base/Super/기반 클래스라고도 부른다.

자식클래스: Derived/Sub/파생 클래스라고도 부른다.

 

 

protected 접근지정자

외부에서는 접근이 불가능 하지만, 파생클래스에서 접근이 가능하도록 하는 접근지정자.

class Base
{
private:   int a;
protected: int b;
public:    int c;
};

 

 

상속에서의 생성자/소멸자 호출순서

기반 클래스 생성자, 파생 클래스 생성자

파생 클래스 소멸자, 기반 클래스 소멸자

 

파생클래스 객체의 객체 생성지 인자가 있어도, 기반 클래스 생성자는 디폴트 생성자를 호출한다.

만약 기반 클래스 생성자를 디폴트가 아닌 다른 생성자를 부르고 싶을때는 아래와 같은 코드 처럼 부른다.

만약 기반 클래스에 디폴트 생성자가 존재하지 않을 경우는, 반드시 파생 클래스에서 기반 클래스의 생성자를 명시 해주어야된다. 안해줄 경우 디폴트 생성자가 없기 때문에 ERROR발생.

기반 클래스의 생성자를 명시적으로 호출하는 코드는 구현부(소스파일)에 작성한다.

 

class Base
{
    int data;
public:
    Base()       { cout << "Base()"    << endl; }
    Base(int a)  { cout << "Base(int)" << endl; }
    ~Base()      { cout << "~Base()"   << endl; }
};

class Derived : public Base
{
public:
    Derived()       { cout << "Derived()"    << endl; }
    // Base(int a) 생성자를 호출하게된다
    Derived(int a) : Base(a) { cout << "Derived(int)" << endl; }
    ~Derived()      { cout << "~Derived()"   << endl; }
};

 

 

protected 생성자

자기자신은 객체를 만들 수 없지만(추상적인 존재), 파생클래스의 객체는 만들 수 있다.

class Animal
{
protected:
    Animal() {}
};

class Dog : public Animal
{
public:
    Dog() : Animal() {} 
};

int main()
{
    // 다음중 에러를 모두 고르세요
    //Animal a;   // error
    Dog    d;   // ok
}

 

다중 상속

클래스가 2개 이상의 기반 클래스로부터 상속되는 것.

C++은 지원하지만, Java는 지원하지 않는다.

 

서로 다른 기반 클래스에 동일 이름의 멤버가 있을때 이름 충돌이 발생한다.

 

class InputFile
{
public:
    void read() {}
    void open() {}
};

class OutputFile
{
public:
    void write(){}
    void open() {}
};

class IOFile : public InputFile, public OutputFile
{    
};

int main()
{
    IOFile file;
    //file.open(); //이름충돌발생
    file.InputFile::open(); //명시해주면 충돌발생하지 않음
}

 

다중상속을 하게되면 Diamond 형태의 상속이 가능하게 된다.

D객체는 A를 상속받는 두개의 클래스 모두가 보유하게 되어서,

D를 보유한 2개의 서브클래스가 메소드로 호출되었을때 어떤 클래스에 속한건지 모호하기 떄문에 정상적으로 수행하지 못한다.

 

class InputFile : virtual public File 
{
public:
    void read() {}
};

virtual 상속을 사용하면 File의 인스턴스가 메모리에 한번만 생성되게된다.

하지만 다이아몬드 상속 문제는 다이아몬드 상속을 피하고 그냥 쓰지 않는게 마음 편하다고 한다...