C++ 챕터 13장 기초정리

2024. 9. 28. 16:09·스터디/C++ 스터디
728x90
반응형

 

13. 클래스의 상속

이 챕터는 객체 지향 프로그래밍의 중요한 목표 중 하나인 코드 재활용의 이점을 설명하고 있다. 큰 프로젝트에서는 처음부터 코드를 작성하는 것보다 이미 성능이 입증된 코드를 재활용하는 것이 더 효율적이며, 개발 시간 절약과 버그 발생 가능성을 줄일 수 있다. C 언어에서는 strlen()이나 rand() 같은 함수 라이브러리를 통해 재활용성을 제공하지만, 라이브러리 함수는 사용자의 특정 요구에 맞게 수정하기 어렵다.

C++에서는 클래스와 클래스 상속을 통해 더 높은 수준의 재활용성을 제공한다. 클래스 라이브러리는 함수 라이브러리보다 통합된 패키지를 제공하며, 상속을 통해 기존 클래스에 기능을 추가하거나 데이터 및 메서드를 확장할 수 있다. 상속을 사용하면 기존 클래스를 수정하지 않고도 새로운 기능을 추가할 수 있어 코드의 재활용성과 확장성이 높아진다.

결론적으로 상속은 새로운 클래스를 설계하는 것보다 간단하고 효율적이며, 코드를 재활용하면서도 기능을 쉽게 확장할 수 있게 해준다.

 

간단한 기초 클래스로부터 시작하자.

 

어떤 클래스를 다른 클래스로부터 상속할 때 오리지널 클래스를 기초 클래스(base class)라 하고, 상속받는 클래스를 파생 클래스(derived 더ass)라 한다. 상속을 설명하기 위해 기초클래스부터 시작하자. Webtown Social Club은 탁구 동호회의 회원

클래스의 상속 정보를 관리하기로 결정했다. 그 클럽의 수석 프로그래머는 Llsting 13.1과 Llsting 13.2에 정의되어 있는 간단한 TableTennisPlayer 클래스를 설계했다.

 

// tabtenn0.cpp -- simple base-class methods
// 탁구 기초 클래스 메서드 구현 파일
// 이 파일은 tabtenn0.h 헤더 파일에 선언된 클래스의 생성자와 멤버 함수들을 정의

#include "tabtenn0.h"   // 클래스 정의를 포함한 헤더 파일을 포함
#include <iostream>     // 표준 입출력 스트림을 사용하기 위한 헤더 파일 포함

// TableTennisPlayer 클래스의 생성자(Constructor) 정의
// 생성자는 객체가 생성될 때 자동으로 호출되어 객체의 멤버 변수를 초기화
// 매개변수: 
// const string & fn : 선수의 이름을 전달, 기본값은 "none"
// const string & ln : 선수의 성을 전달, 기본값은 "none"
// bool ht : 선수가 테이블을 가지고 있는지 여부를 전달, 기본값은 false
// 초기화 목록을 사용하여 멤버 변수 firstname, lastname, hasTable을 전달받은 값으로 초기화
TableTennisPlayer::TableTennisPlayer(const string & fn, const string & ln, bool ht)
    : firstname(fn),   // firstname 멤버를 매개변수 fn으로 초기화
      lastname(ln),    // lastname 멤버를 매개변수 ln으로 초기화
      hasTable(ht)     // hasTable 멤버를 매개변수 ht로 초기화
{
    // 초기화는 초기화 목록에서 이미 완료되었기 때문에 함수 본문은 비어 있음
    // 이와 같이 초기화 목록을 사용하는 것은 효율적이며, 멤버 변수를 생성 시점에 바로 설정할 수 있음
}

// Name() 멤버 함수 정의
// 이 함수는 콘솔에 선수의 성(lastname)과 이름(firstname)을 출력함
// const 함수로 정의되어 있어, 객체의 상태(멤버 변수)를 변경하지 않음을 보장
void TableTennisPlayer::Name() const
{
    // std::cout: 표준 출력 스트림 객체, 콘솔에 데이터를 출력하는 데 사용
    // lastname을 먼저 출력한 후 ", "와 firstname을 출력
    // 예: 홍길동의 경우 "길동, 홍" 형식으로 출력됨
    std::cout << lastname << ", " << firstname;
    // 여기서는 출력을 위해 std::cout을 사용하고 있으므로, 네임스페이스를 명시함 (std::)
}
 

 

 

TableTennisPlayer 클래스가 하는 일은 오로지 선수의 이름과 그 선수가 탁구대를 소유하고 있는지 추적하는 것이다. 여기 몇 가지 주목해야 할 내용이 있다. 첫째, 클래스는 이름을 소유하기 위해서 표준 string 클래스를 시용한다. 이렇게 하는 것이 문자열을 사용하는 것보다 편리하고 탄력적이며 안전한 방법이다. 표준 string클래스를사용하는것이 12장의 ‘‘클래스와동적 메모리 대입 ”에서 다루는 String 클래스에 대한 내용보다 더 전문성을 지닌다. 둘째, 생성자는 12장에서 소 개된 멤버 초기자 리스트 구문을사용한다. 독자는 다음과 같이 진행할수 있다.

 

TableTennisPlayer: :TableTennisPlayer (const string & fn, const string & ln, bool ht)

{

firstname = fn;

lastname = ln;

hasTable = ht;

}

 

그러나 이러한 접근 방식은 처음 firstname에 대한 디폴트 string 생성자를 부른 다음, string 연산자를 불러서 firstname을 fn으로 다시 세팅하는 효 과가 있다. 그러나 멤버 초기자 리스트 구문은 string 복사 생성자를 사용하여 firstname을 fn으로 초기화하는 한 단계를 생략해 준다. Listing 13.3은 이 간단한 클래스가 어떻게 동작하는지를 보여 준다.

 

// usett0.cpp -- using a base class
// 기본 클래스를 사용하는 예시
// 이 파일은 TableTennisPlayer 클래스를 사용하여 객체를 생성하고, 그 객체의 정보를 출력

#include <iostream>      // 표준 입출력 스트림을 사용하기 위한 헤더 파일 포함
#include "tabtenn0.h"    // TableTennisPlayer 클래스 정의를 포함한 사용자 정의 헤더 파일 포함

// main 함수 정의
// 프로그램의 진입점으로, 객체를 생성하고 해당 객체의 멤버 함수를 호출하여 정보를 출력
int main(void)  // 반환값이 없는 main 함수
{
    // std 네임스페이스에서 cout을 가져와서 사용 (표준 출력 스트림)
    using std::cout;

    // TableTennisPlayer 클래스의 객체 player1 생성
    // "Chuck"이라는 이름과 "Blizzard"라는 성을 가진 선수로, hasTable 값을 true로 설정 (탁구 테이블을 소유함)
    TableTennisPlayer player1("Chuck", "Blizzard", true);

    // TableTennisPlayer 클래스의 객체 player2 생성
    // "Tara"라는 이름과 "Boomdea"라는 성을 가진 선수로, hasTable 값을 false로 설정 (탁구 테이블을 소유하지 않음)
    TableTennisPlayer player2("Tara", "Boomdea", false);

    // player1 객체의 Name() 함수를 호출하여 콘솔에 선수의 성과 이름을 출력
    player1.Name();

    // player1 객체가 테이블을 소유하고 있는지 여부를 확인
    if (player1.HasTable())  // HasTable() 함수는 테이블 소유 여부(true/false)를 반환
        cout << ": has a table.\n";  // true일 경우 "has a table" 메시지를 출력
    else
        cout << ": hasn't a table.\n";  // false일 경우 "hasn't a table" 메시지를 출력

    // player2 객체의 Name() 함수를 호출하여 콘솔에 선수의 성과 이름을 출력
    player2.Name();

    // player2 객체가 테이블을 소유하고 있는지 여부를 확인
    if (player2.HasTable())  // player2가 테이블을 소유하고 있을 경우
        cout << ": has a table.\n";  // "has a table" 메시지를 출력
    else
        cout << ": hasn't a table.\n";  // 테이블을 소유하지 않을 경우 "hasn't a table" 메시지를 출력

    // std::cin.get();  // 주석 처리된 부분으로, 사용자가 입력을 할 때까지 대기하는 역할. 디버깅 목적으로 사용 가능
    return 0;  // 프로그램이 정상적으로 종료되었음을 의미하는 반환값
}
 

다음은 listing 13.1, 13.2, 13.3으로 구성된 프로그램의 실행 예이다.

 

Blizzard, Chuck: 탁구대가 있다.

Boomdea , Tara : 탁구대가 없다 .

 

프로그램이 C 스타일 suing 구문으로 된 생성자를 사용하는 프로그램을 주목해

보자.

 

TableTennisPlayer playerl ( "chuck" , "Blizzard" , 참) ;

TableTennisPlayer player2 ( "Tara" , "Boomdea" , 거짓) ;

 

그러나 생성자 선언에서 매개변수들의 타입은 canst string & 형으로 선언되었다 이것은 잘못 연결된 자료형이지만, 표준 string 클래스는 12장의 String클래스와 흡사하게 canst char* 형의 매개변수를 지닌 생성자를 가지고 있다 그리고 이 생성자는 C 스타일 string으로 초기화된 string 객체를 생성하기 위해 자동으로 사용된댜 즉 TableTennisPlayer 생성자에 대한 매개변수

로 C 스타일 string이나 string 객체 두 가지 중 어떤 것을 사용해도 무방하다.string 객체는 canst string & 형의 매개변수를 지닌 string 생성지를 불러오고, C 스타일 string은 canst char* 형의 매개변수를 지닌 string 생성자를불러온다.

 

클래스 파생시키기

 

Webtown Social Club 소속의 일부 회원들이 지역 탁구 대회에 선수로 참가하였다.

그들은 그 대회에서 거둔 랭킹을 포함하는 클래스를 요구한다. 이 경우에 무에서부터 시작하지 않고, TableTennisPlayer로부터 원하는 클래스를 파생시킬 수 있다 첫 딘계는 RatedPlayer 클래스가 TableTennisPlayer 로부디 파생되었다는 사실을 알 수 있도록 선언하는 것이다.

 

// RatedPlayer는 TableTennisPlayer 기초 클래스로부터 파생된다

class RatedPlayer : public TableTennisPlayer

{

...

{

 

 

여기서 콜론은 RatedPlayer 클래스가 TableTennisPlayer 클래스에 기초를 두고 있다는 것을 나타낸다. 이 특별한 표제는 TableTennisPlayer가 public 기초 클래스라는 것을 나타낸다 이것을 public 파생(public detivation)이라 한다 파

생 클래스의 객체는 기초 클래스의 객체를 합병한다. public 파생에서는 기초 클래스의 public 멤버들이 파생 클래스의 public 멤버가 된다. 기초 클래스의 private 부분들도 파생 클래스의 일부가 된다. 그러나 그들은 기초 클래스의 protected 메서드와 public 메서드를 통해서만 접근할 수 있다. (protected 멤버에 대해서는 잠시 후에 다룬다.)

 

이것으로 우리는 무엇을 할 수 있을까? RatedPlayer 객체를 선언참다면 그 객체는 다음과 같은 특별한 속성을 가진다.

 

• 파생 클래스형의 객체 안에는 기초 클래스형의 데이터 멤버들이 저장된다. (파생클래스는 기초 클래스의 구현들을 상속받는다.)

• 파생 클래스형의 객체는 기초 클래스형의 메서드들을 사용할 수 있다. (파생 클래스는기초클래스의 인터페이스를상속받는다.)

 

그래서, RatedPlayer 객체는 각 선수의 퍼스트네임 (first name)과 라스트네임(last name), 그가 탁구대를 소유하고 있는지 여부를 저장할 수 있다.

 

RatedPlayer 객체는 TableTennisPlayer로부터 상속받은 Name ( ) ,

HasTable ( ) , ResetTable ( ) 메서드를 사용할 수 있다. (그림 13.1 참조)

이와 같은 상속받은 기능에 무엇을 추가할 필요가 있을까?

 

• 파생 클래스는 자기 자신의 생성지를- 필요로 한다.

• 파생 클래스는 부가적인 데이터 멤버들과 멤버 함수들을 필요한 만큼 추가할 수있다.

 

특별히 우리의 RatedPlayer 파생 클래스는, 탁구 대회에서 거둔 랭킹을 저장하기 위해 하나의 데이터 멤버가 더 필요하다. 랭킹을 알아내는 메서드와, 랭킹을 다시 설정하는 메서드가 필요하댜 그러므로 그 클래스 선언은 다음과 같을 것이다.

 

// 간단한파생클래스

 

class RatedPlayer : public TableTennisPlayer
{
private:
unsigned int rating; // 추가 데이터 멤버
public:
RatedPlayer(unsigned int r = 0, const char * fn = "none",const string & ln = "none", bool ht = fal se);
RatedPlayer(unsigned int r, const Tab上eTennisPlayer & tp);
unsigned int Rating ( ) { return rating; } // 추가 메서드
void ResetRating(unsigned int r) { rating = r; }
// 추가 메서드
} ;
 

 

여기서 생성자들은 새로 추가된 데이터 멤버와(있을 경우에), 상속받은 데이 터 멤버들에 데이터를 제공해야 한다. 첫 번째 RatedPlayer 생성자는 각각 의 데이터 멤버들에 대해서 개별적인 형식 매개변수를 사용한다. 그리고 두 번째 RatedPlayer 생성지는 하나의 TableTennisPlayer 매개변수를 사용한댜 그 매개변수는 세 가지 항목(firstname, lastname, hasTable)을 하나의 묶음 으로 결합한다

1. is-a 관계로 상속하기

 

설명: 상속에서 is-a 관계는 파생 클래스가 기초 클래스의 모든 속성과 메서드를 상속받아 기초 클래스처럼 동작할 수 있다는 것을 의미한다.

is-a 관계는 파생 클래스가 기초 클래스의 확장된 형태이거나, 기초 클래스의 구체적인 한 종류로 볼 수 있는 상황에서 사용된다.

 

코드 예제: TableTennisPlayer 클래스와 이를 상속받는 RatedPlayer 클래스를 통해 이 개념을 살펴볼 수 있다.

class TableTennisPlayer {
private:
    string firstname;
    string lastname;
    bool hasTable;
public:
    TableTennisPlayer(const string &fn = "none", const string &ln = "none", bool ht = false)
        : firstname(fn), lastname(ln), hasTable(ht) {}
    void Name() const {
        cout << lastname << ", " << firstname;
    }
    bool HasTable() const { return hasTable; }
    void ResetTable(bool v) { hasTable = v; }
};

class RatedPlayer : public TableTennisPlayer {
private:
    unsigned int rating;
public:
    RatedPlayer(unsigned int r = 0, const string &fn = "none", const string &ln = "none", bool ht = false)
        : TableTennisPlayer(fn, ln, ht), rating(r) {}
    unsigned int Rating() const { return rating; }
    void ResetRating(unsigned int r) { rating = r; }
};
 

여기서 RatedPlayer 클래스는 TableTennisPlayer 클래스를 상속받아 기초 클래스의 이름과 탁구대 소유 여부를 관리하는 기능을 그대로 사용하면서, 추가적으로 rating이라는 멤버 변수를 사용해 선수의 등급을 관리하는 기능을 추가한다. 이 클래스 관계는 "is-a" 관계를 나타내며, RatedPlayer는 TableTennisPlayer의 일종이라는 의미를 가진다

 

이점: 이러한 is-a 관계 상속은 기존의 클래스를 재사용함으로써 코드 중복을 줄이고 유지보수를 용이하게 만든다. 기초 클래스의 동작을 그대로 물려받고 필요할 경우 파생 클래스에서 이를 확장하거나 수정할 수 있다.


2. protected 접근

 

설명: protected 접근 제어자는 기초 클래스의 멤버가 파생 클래스에서 public처럼 접근할 수 있게 하면서도 외부에서는 private처럼 접근을 제한하는 방식이다. 이를 통해 파생 클래스는 기초 클래스의 중요한 멤버에 접근할 수 있지만, 외부에서의 접근은 차단된다.

 

코드 예제:

class TableTennisPlayer {
protected:
    string firstname;
    string lastname;
    bool hasTable;
public:
    TableTennisPlayer(const string &fn = "none", const string &ln = "none", bool ht = false)
        : firstname(fn), lastname(ln), hasTable(ht) {}
    void Name() const {
        cout << lastname << ", " << firstname;
    }
    bool HasTable() const { return hasTable; }
    void ResetTable(bool v) { hasTable = v; }
};

class RatedPlayer : public TableTennisPlayer {
public:
    RatedPlayer(unsigned int r = 0, const string &fn = "none", const string &ln = "none", bool ht = false)
        : TableTennisPlayer(fn, ln, ht) {}
    void ShowName() const {
        Name(); // Name() 메서드는 protected 멤버 접근
    }
};
 

 

위 코드에서 TableTennisPlayer 클래스의 멤버 firstname, lastname, hasTable은 protected로 선언되어 있기 때문에 파생 클래스인 RatedPlayer에서 접근이 가능하다. 하지만 외부에서는 해당 멤버 변수에 직접 접근할 수 없다. 이 방식은 상속을 통해 기초 클래스의 중요한 데이터를 안전하게 보호하면서도 파생 클래스에서 유연하게 사용할 수 있도록 한다.


3. 업캐스팅과 다운캐스팅

 

설명: 업캐스팅은 파생 클래스의 객체를 기초 클래스의 포인터나 참조로 처리하는 것이며, 다운캐스팅은 그 반대로 기초 클래스의 포인터나 참조를 파생 클래스의 것으로 변환하는 작업을 의미합니다. 업캐스팅은 암시적으로 가능하지만, 다운캐스팅은 명시적으로 캐스팅해야 합니다.

 

코드 예제:

TableTennisPlayer player1("Chuck", "Blizzard", true);
RatedPlayer rplayer1(1140, "Mallory", "Duck", true);

// 업캐스팅: 파생 클래스 객체를 기초 클래스 포인터로 변환
TableTennisPlayer *pt = &rplayer1;
pt->Name(); // 기초 클래스 메서드 호출

// 다운캐스팅: 기초 클래스 포인터를 파생 클래스 포인터로 변환
RatedPlayer *pr = static_cast<RatedPlayer*>(pt);
cout << pr->Rating(); // 파생 클래스 메서드 호출
 

이 예제에서는 RatedPlayer 객체를 TableTennisPlayer 포인터로 업캐스팅하여 Name() 메서드를 호출할 수 있습니다. 반대로 다운캐스팅을 사용하여 RatedPlayer 타입으로 변환하고, 파생 클래스의 고유 메서드인 Rating()을 호출할 수 있습니다. 다운캐스팅은 명시적으로 static_cast와 같은 방법을 사용해야 합니다

이점: 업캐스팅과 다운캐스팅을 사용하면 상속 구조에서 다형성을 활용할 수 있으며, 파생 클래스 객체를 기초 클래스 인터페이스로 다룰 수 있게 됩니다. 이를 통해 다양한 객체를 일관된 방식으로 처리할 수 있습니다.


4. 초기 결합(정적 결합)과 말기 결합(동적 결합)

설명: 초기 결합(정적 결합)은 컴파일 시점에 함수 호출이 결정되며, 말기 결합(동적 결합)은 실행 중 객체의 실제 타입에 따라 적절한 함수가 선택되는 방식입니다. 동적 결합은 주로 가상 함수를 통해 구현됩니다.

코드 예제:

class TableTennisPlayer {
public:
    virtual void Name() const {
        cout << "Table Tennis Player";
    }
};

class RatedPlayer : public TableTennisPlayer {
public:
    void Name() const override {
        cout << "Rated Player";
    }
};

TableTennisPlayer player;
RatedPlayer ratedPlayer;

TableTennisPlayer &ref = ratedPlayer; // 업캐스팅
ref.Name(); // 동적 결합: RatedPlayer의 Name() 호출
 

 

위 예제에서 Name() 함수는 TableTennisPlayer에서 가상 함수로 선언되었습니다. 이를 통해 ref가 RatedPlayer 객체를 참조하더라도, 런타임에 적절한 함수인 RatedPlayer의 Name()이 호출됩니다. 이것이 동적 결합의 예시입니다. 가상 함수가 아닌 경우 정적 결합으로 처리되며, 참조의 타입에 따라 함수가 호출됩니다

이점: 동적 결합을 사용하면 상속 관계에서 다형성을 구현할 수 있습니다. 프로그램이 실행되는 동안 객체의 실제 타입에 맞는 메서드가 호출되므로 유연한 코딩이 가능합니다.


5. 순수 가상 함수

설명: 순수 가상 함수는 = 0으로 선언되며, 파생 클래스에서 반드시 구현해야 합니다. 이를 통해 기초 클래스는 추상 클래스로 선언되며 객체를 직접 생성할 수 없습니다.

코드 예제:

class Player {
public:
    virtual void Play() const = 0; // 순수 가상 함수
};

class TableTennisPlayer : public Player {
public:
    void Play() const override {
        cout << "Playing table tennis.";
    }
};
 

 

Player 클래스는 추상 클래스이며, 순수 가상 함수 Play()를 포함하고 있습니다. 이를 상속받는 TableTennisPlayer 클래스는 Play() 함수를 반드시 구현해야 합니다. 추상 클래스는 직접 인스턴스화할 수 없고, 파생 클래스에서 구체적인 동작을 정의해야 합니다


6. 다른 클래스로부터 public으로 파생된 클래스

설명: public 상속은 파생 클래스가 기초 클래스의 public 멤버를 상속받으며, 파생 클래스에서도 이를 그대로 사용할 수 있게 만듭니다. 상속받은 클래스는 기초 클래스의 public 인터페이스를 그대로 유지합니다.

코드 예제:

 

class TableTennisPlayer {
public:
    void Name() const {
        cout << "Table Tennis Player";
    }
};

class RatedPlayer : public TableTennisPlayer {
public:
    void ShowRating() const {
        cout << "Player rating: " << rating;
    }
};
 

 

RatedPlayer는 TableTennisPlayer로부터 public으로 상속받아, Name() 메서드를 파생 클래스에서도 그대로 사용할 수 있습니다. public 상속을 통해 파생 클래스에서 기초 클래스의 기능을 확장할 수 있습니다


7. 생성자 멤버 초기자 리스트

설명: 생성자 멤버 초기자 리스트는 멤버 변수를 생성자에서 초기화하는 구문입니다. 이는 생성자 내부에서 변수를 초기화하는 것보다 효율적이며, 특히 객체나 참조 타입 멤버 변수를 초기화할 때 필수적입니다.

코드 예제:

class TableTennisPlayer {
public:
    TableTennisPlayer(const string &fn, const string &ln, bool ht)
        : firstname(fn), lastname(ln), hasTable(ht) {}
private:
    string firstname;
    string lastname;
    bool hasTable;
};
 

이 예제에서 초기자 리스트를 사용하여 firstname, lastname, hasTable 변수를 초기화합니다. 초기자 리스트는 생성자 내부에서 값을 할당하는 것보다 효율적이며, 특히 클래스의 복사 생성자나 상속에서 중요한 역할을 합니다


8. 가상 멤버 함수

설명: 가상 멤버 함수는 파생 클래스에서 재정의될 수 있으며, 기초 클래스의 포인터나 참조를 통해 파생 클래스의 함수를 호출할 수 있습니다. 이는 런타임에 동적 결합을 통해 적절한 함수가 선택됩니다.

코드 예제:

class TableTennisPlayer {
public:
    virtual void Name() const {
        cout << "Table Tennis Player";
    }
};

class RatedPlayer : public TableTennisPlayer {
public:
    void Name() const override {
        cout << "Rated Player";
    }
};

TableTennisPlayer *ptr = new RatedPlayer();
ptr->Name(); // RatedPlayer의 Name()이 호출됨
 

이 예제에서 TableTennisPlayer의 Name()은 가상 함수로 선언되었기 때문에, 기초 클래스 포인터로도 파생 클래스의 Name() 메서드를 호출할 수 있습니다. 이를 통해 런타임에 적절한 함수가 선택됩니다


9. 추상화 기초 클래스

설명: 추상화 기초 클래스(ABC)는 순수 가상 함수를 포함하여 객체를 직접 생성할 수 없는 클래스를 의미합니다. 주로 공통적인 인터페이스를 정의하는 데 사용되며, 파생 클래스에서 구체적인 구현을 제공합니다.

코드 예제:

class Player {
public:
    virtual void Play() const = 0; // 추상화 기초 클래스
};
 

Player 클래스는 추상 클래스이며, 객체를 생성할 수 없습니다. 파생 클래스에서 이 추상 메서드를 반드시 구현해야 합니다


10. public 상속은 언제 어떻게?

 

설명: public 상속은 파생 클래스가 기초 클래스의 `public` 멤버를 그대로 상속받아 외부에서도 이를 그대로 사용할 수 있도록 할 때 사용됩니다. 이는 주로 파생 클래스가 기초 클래스의 확장이나 구체적인 형태일 때 적합합니다.

 

코드 예제:

  class Fruit {
  public:
      void Info() const {
          cout << "This is a fruit.";
      }
  };

  class Banana : public Fruit {
  public:
      void Peel() const {
          cout << "Peeling banana.";
      }
  };

  Banana myBanana;
  myBanana.Info();  // Fruit의 메서드 호출
  myBanana.Peel();  // Banana의 메서드 호출
  ```
 
 

여기서 `Banana`는 `Fruit` 클래스로부터 `public` 상속받아, `Fruit`의 `Info()` 메서드를 그대로 사용할 수 있습니다. public 상속은 파생 클래스가 기초 클래스와 `is-a` 관계를 형성할 때 적합

 

public 상속은 파생 클래스가 기초 클래스와 `is-a` 관계를 형성할 때 적합하다

 

 

728x90
반응형

'스터디 > C++ 스터디' 카테고리의 다른 글

C++ 챕터 12장 기초정리  (0) 2024.09.28
C++ 챕터 11장 기초정리  (0) 2024.09.26
C++ 챕터 10장 기초정리  (0) 2024.09.25
C++ 챕터 1~ 9장 기초 정리  (0) 2024.09.24
'스터디/C++ 스터디' 카테고리의 다른 글
  • C++ 챕터 12장 기초정리
  • C++ 챕터 11장 기초정리
  • C++ 챕터 10장 기초정리
  • C++ 챕터 1~ 9장 기초 정리
DDD Developer
DDD Developer
  • DDD Developer
    DDD
    DDD Developer
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 개발 일지
        • C언어
        • python 파이썬
        • 기타
        • 데이터베이스
        • TCP 와 IP
        • C++
        • QT
        • C#
      • 스터디
        • C언어
        • python 파이썬
        • TCP 와 IP
        • C++ 스터디
        • QT 스터디
      • 프로젝트
      • 문제풀이
        • C언어
        • python 파이썬
  • 인기 글

  • 최근 글

  • 반응형
  • hELLO· Designed By정상우.v4.10.2
DDD Developer
C++ 챕터 13장 기초정리
상단으로

티스토리툴바