Programming Language/C++

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

lumana 2024. 6. 27. 18:18

상속이 필요한 이유(1)

상속이 필요한 이유에 대해서 알아보자

example) 메신저 프로그램

  • 텍스트, 이미지 등의 포맷을 갖는 데이터를 전송한다

  • 상속을 사용하지 않고 코드를 작성해보자

// 상속을 사용하지 않은 코드
#include <iostream>
#include <string>

using namespace std;

class Image {
   public:
    operator string() {
        return "사진";
    }
};
class TextMessage {
   public:
    TextMessage(int sendTime, string sendName, string text) {
        this->sendTime = sendTime;  // this->sendTime을 안쓰고 sendTime을 쓰면 매개변수 값을 매개변수에 넣는 꼴.
        this->sendName = sendName;
        this->text = text;
    }

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

   private:
    int sendTime;
    string sendName;
    string text;
};

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

    int GetSendTime() const { return sendTime; }
    string GetSendName() const { return sendName; }
    Image *GetImage() const { return image; }

   private:
    int sendTime;
    string sendName;
    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;
}

참고) 실제로 Image를 출력하는 기능을 구현하기에는 복잡하므로 Image 타입을 string으로 형변환하여 사용하도록 구현하였다.

  • ImageMessage 클래스와 TextMessage 클래스를 따로 선언하였다

    • SendName, SendTime을 공통적으로 가지고 있음

    • 이미지 메시지 클래스와 텍스트 메시지 클래스의 공통 부모를 만들어보자.

      • 부모에 두 공통멤버를 넣고, 부모에서 이미지 메시지와 텍스트 메시지에 상속해주는 방식으로 구현할 수 있다.
  • 상속을 이용해서 사용한 코드

// 상속을 사용한 코드

#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;
};
  • 공통 멤버를 부모 클래스로 뽑아내고 멤버 공개 level을 protected로 바꾸는 것 보단, private을 유지하면서 부모 클래스의 생성자나 멤버 함수를 통해 접근하는 방식을 사용하자

    • 정보 은닉(Encapsulation), 클래스 불변성 유지, 캡슐화, 유지보수성에서 훨씬 유리하다
  • 자식 클래스의 생성자가 호출될 때, 컴파일러는 가장 먼저 부모 클래스의 생성자를 호출하려고 시도하게 된다. 이 때 생성자 오버로딩을 통해 생성자가 여러 개 존재하는 경우가 있을 수 있으므로, 모호성이 발생하지 않도록 자식 클래스의 생성자에서 부모 클래스의 어떤 생성자를 호출할 것인지를 명시해줘야 한다

    • TextMessage(int sendTime, string sendName, string text) : Message(sendTime, sendName)

    • ImageMessage(int sendTime, string sendName, Image *image) : Message(sendTime, sendName)

    • 이전에 다뤘던 생성자 위임의 문법을 사용하면 된다

  • 이렇게 클래스 구조를 수정했지만, 메인함수에서 아무것도 수정하지 않아도 동일한 결과를 얻는다

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;
}
  • 여기서 알 수 있는 상속의 장점 : 자식 클래스를 일일히 다 수정하지 않아도, 공통적인 내용을 뽑아낸 부모 클래스를 수정하면 모든 자식 클래스가 바뀌게 된다.