Programming Language/C++

[C++] 상속이 필요한 이유(2)

lumana 2024. 6. 27. 22:10

상속이 필요한 이유(2)

  • 상속이 필요한 이유(1) 코드
#include <iostream>
#include <string>

using namespace std;

class Message {
   public:
    Message(int sendTime, string sendName) {
        this->sendTime = sendTime;  // this->sendTime을 안쓰고 sendTime을 쓰면 매개변수 값을 매개변수에 넣는 꼴.
        this->sendName = sendName;
    }

    int GetSendTime() const { return sendTime; }
    string GetSendName() const { return sendName; }

   private:
    int sendTime;
    string sendName;
};

class Image {
   public:
    operator string() {
        return "사진";
    }
};
class TextMessage : public Message {
   public:
    TextMessage(int sendTime, string sendName, string text) : Message(sendTime, sendName) {
        this->text = text;
    }

    string GetText() const { return text; }

   private:
    string text;
};

class ImageMessage : public Message {
   public:
    ImageMessage(int sendTime, string sendName, Image *image) : Message(sendTime, sendName) {
        this->image = image;
    }

    Image *GetImage() const { return image; }

   private:
    Image *image;
};

int main() {
    // 두들 : "안녕"
    // 두들 : "강아지 사진"
    Image *p_dogImage = new Image();
    TextMessage *hello = new TextMessage(10, "두들", "안녕");
    ImageMessage *dog = new ImageMessage(20, "두들", p_dogImage);

    cout << "보낸 시간 : " << hello->GetSendTime() << endl;
    cout << "보낸 사람 : " << hello->GetSendName() << endl;
    cout << "내용 : " << hello->GetText() << endl;
    cout << endl;

    cout << "보낸 시간 : " << dog->GetSendTime() << endl;
    cout << "보낸 사람 : " << dog->GetSendName() << endl;
    cout << "내용 : " << (string)*dog->GetImage() << endl;
    cout << endl;

    delete hello;
    delete dog;
    delete p_dogImage;
}
  • 출력 하는 부분(보낸 시간, 보낸 사람, 내용)이 공통적으로 존재하므로 printMessage라는 함수를 만들어서 처리해보자
void printMessage(TextMessage *m) {
    cout << "보낸 시간 : " << m->GetSendTime() << endl;
    cout << "보낸 사람 : " << m->GetSendName() << endl;
    cout << "내용 : " << m->GetText() << endl;
    cout << endl;
}

void printMessage(ImageMessage *m) {
    cout << "보낸 시간 : " << m->GetSendTime() << endl;
    cout << "보낸 사람 : " << m->GetSendName() << endl;
    cout << "내용 : " << (string)*m->GetImage() << endl;
    cout << endl;
}

int main() {
    /* 생략 */
    printMessage(hello); // TextMessage
    printMessage(dog); // ImageMessage
}
  • 위 코드를 오버라이딩을 통해서 효율적으로 개선해보자
  1. 부모 클래스 타입 Message의 포인터를 printMessage 함수의 파라미터 타입이 되도록 변경
void printMessage(Message *m) {  // 상속과 오버라이딩을 통해 오버로딩을 하지 않아도 하나의 함수로 다 출력 가능.
    cout << "보낸 시간 : " << m->GetSendTime() << endl;
    cout << "보낸 사람 : " << m->GetSendName() << endl;
    // cout << "내용 : " << m->GetText() << endl;
    cout << "내용 : " << m->GetContent() << endl;
    cout << endl;
}
  • GetText(), GetImage() 대신 Image, Text 등의 Content를 반환시킬 수 있는 GetContent() 함수를 부모 클래스(Message)에 가상 함수로 선언한 후 오버라이딩하자.

    • 부모 클래스에서 반환할 Content가 존재하지는 않지만, 만약에 부모 클래스에서 GetContent 함수를 실행하면 빈 문자열을 반환하도록 구현
class Message {
   /* 생략 */
    virtual string GetContent() const { return ""; } // 추가된 코드
   /* 생략 */
};

class TextMessage {
   /* 생략 */
      string GetText() const { return text; } // 기존에 존재하던 코드임
    string GetContent() const { return text; } // 추가된 코드
   /* 생략 */
};

class ImageMessage {
   /* 생략 */
      Image *GetImage() const { return p_image; } // 기존에 존재하던 코드임
    string GetContent() const { return (string)*p_image; } // 추가된 코드
   /* 생략 */
}

void printMessage(Message *m) {  // 상속과 오버라이딩을 통해 오버로딩을 하지 않아도 하나의 함수로 다 출력 가능.
    cout << "보낸 시간 : " << m->GetSendTime() << endl;
    cout << "보낸 사람 : " << m->GetSendName() << endl;
    cout << "내용 : " << m->GetContent() << endl; // GetContet()에서 동적바인딩이 일어남
    cout << endl;
}
  • 상속, 오버라이딩, 동적 바인딩을 통해서 여러 printMessage 함수를 하나의 함수로 만들었다.

  • 아래와 같이 포인터 뿐만 아니라 레퍼런스를 통해서 오버라이딩 할 수도 있다.

void printMessage(const Message &m) {  // 레퍼런스도 동적바인딩 가능, 만약 레퍼런스 없이 하면 객체가 복사되어, Message의 GetContent출력하게됨.
    cout << "보낸 시간 : " << m.GetSendTime() << endl;
    cout << "보낸 사람 : " << m.GetSendName() << endl;
    cout << "내용 : " << m.GetContent() << endl;  // 가상함수 동적바인딩.
    cout << endl;
}

int main(void) {
        Image *p_dogImage = new Image();
    TextMessage *hello = new TextMessage(10, "두들", "안녕");
    ImageMessage *dog = new ImageMessage(20, "두들", p_dogImage);

    printMessage(*hello);
    printMessage(*dog);
}
  • void printMessage(Message m)로 선언하게 되면, 매개변수로 전달되는 과정에서 Message로 바뀌어버리기 때문에, Message 클래스의 GetContent() 멤버 함수가 호출된다

  • 메시지를 딱 2개만 보내는 상황은 거의 발생하지 않는다. 엄청 많은 메시지가 전송될텐데, 일일히 객체를 하나 씩 선언하고, 일일히 printMessage()를 호출할 수는 없다.

    • 메시지를 담는 배열을 만들어보자

      • 포인터 배열을 통해서 구현
int main() {
    // 두들 : "안녕"
    // 두들 : "강아지 사진"
    Image *p_dogImage = new Image();

    Message *messages[] = {
        // 포인터배열을 통해 여러 메세지 가리키키
        new TextMessage(10, "두들", "안녕"),
        new TextMessage(11, "두들", "안녕"),
        new TextMessage(12, "두들", "안녕"),
        new ImageMessage(20, "두들", p_dogImage)};

    for (Message *m : messages) {
        printMessage(*m);
    }

    TextMessage *hello = new TextMessage(10, "두들", "안녕");
    ImageMessage *dog = new ImageMessage(20, "두들", p_dogImage);

    printMessage(*hello);
    printMessage(*dog);

    delete hello;
    delete dog;
    delete p_dogImage;

    for (Message *m : messages) {
        delete m;
    }
}