웅재의 코딩세상
C++ 상속 본문
객체 지향 프로그래밍에서 가장 중요한 목적중 하나는 재사용 할 수 있는 코드를 제공하는 것이다.
함수의 이름을 호출하는 것만으로 코드의 반복을 효율적으로 다루었다.
클래스를 확장하고 수정하기 위한 더 강력한 도구인 상속을 제공한다.
상속의 기능
- 기존에 선언되어 있는 클래스에 새로운 기능을 추가할 수 있다.
- 클래스가 가지고 있는 private 멤버의 데이터 변수들에 새로운 데이터를 추가할 수도 있다.
- 함수가 동작하는 방식을 변경할 수 있다.
class NewTime : public Time{
private:
int day;
public:
NewTime();
NewTime(int, int, int);
void print();
};
NewTime::NewTime() : Time(){ //Time()이 멤버 초기화리스트
day = 0;
}
NewTime::NewTime(int, int, int) : Time(h,m) {
day = d;
}
void NewTime::print(){
cout << "일 : " << day << endl;
show();
};
int main(){
NewTime temp1();
NewTime temp2(3,30,2);
temp2.print(); // 2일 3시간 30분으로 출력된다.
}
- 파생 클래스형의 객체 안에는 기초 클래스형의 데이터 멤버들이 저장된다.
- 파생 클래스형의 객체는 기초 클래스형의 매서드를 사용할 수 있다.
- 파생 클래스는 자기 자신의 생성자를 필요로 한다.
- 파생 클래스는 부가적인 데이터 멤버들과 멤버 함수들을 임의로 추가할 수 있다.
파생 클래스는 원형 클래스의 public 멤버 함수들에게 접근할 수 있지만 private 멤버 변수들에게는 접근할 수 없다.
-> 원형 클래스에서 public 영역에 private변수를 리턴하는 함수를 만든다면 구현할 수 있다.
프로그램이 파생 클래스의 개체를 생성할 때 먼저 기초 클래스의 개체를 생성한다
기초 클래스의 객체에다 덧붙이는 방법으로 파생 클래스의 객체를 생성하게 되는 것이다.
프로그램이 파생 클래스의 생성자의 몸체 안으로 들어가기 전에 기초 클래스의 객체가 먼저 생성되어야 한다는 것을 의미한다.
다형 상속
처해지는 상황에 따라서 동작을 다르게 하는 것을 다형이라고 한다.
->기초 클래스 메소드를 파생 클래스에서 다시 정의해야 한다.
-> show 함수를 재정의 해보자
class Time
{
private:
int hours;
int mins;
public:
Time();
Time(int, int);
void addHours(int);
void addMins(int);
Time operator+(Time&);
Time operator*(int);
virtual void show(); // 가상 메서드가 된다. 서로 독립된 두개의 정의가 존재한다고 알려줌
~Time();
int getHour() { return hours; } // NewTime객체에서 사용할 수 있게 private 멤버 변수를 리턴해준다.
int getMins() { return mins; }
friend Time operator*(int n, Time& t) {
return t * n;
}
friend std::ostream& operator<<(std::ostream&, Time&);
};
class NewTime : public Time {
private:
int day;
public:
NewTime();
NewTime(int, int, int);
void show();
};
Time::Time()
{
hours = mins = 0;
}
Time::Time(int h, int m) {
hours = h;
mins = m;
}
void Time::addHours(int h) {
hours += h;
};
void Time::addMins(int m) {
mins += m;
hours += mins / 60;
mins %= 60;
};
Time Time::operator+(Time& t) {
Time sum;
sum.mins = mins + t.mins;
sum.hours = hours + t.hours;
sum.hours += sum.mins / 60;
sum.mins %= 60;
return sum;
};
void Time::show() {
std::cout << "시간 : " << hours << std::endl;
std::cout << "분 : " << mins << std::endl;
}
Time::~Time()
{
}
Time Time::operator*(int n) {
Time result;
long resultMin = hours * n * 60 + mins * n;
result.hours = resultMin / 60;
result.mins = resultMin % 60;
return result;
}
std::ostream& operator<<(std::ostream& os, Time& t) {
os << t.hours << "시간 " << t.mins << "분";
return os;
}
NewTime::NewTime() : Time(){
day = 0;
}
NewTime::NewTime(int h, int m, int d) : Time(h,m){
day = d;
}
void NewTime::show() {
std::cout << "일 : " << day << std::endl;
std::cout << "시간 : " << getHour() << std::endl;
std::cout << "분 : " << getMins() << std::endl;
}
virtual (가상 메소드)
서로 독립된 두 개의 메소드 정의가 있다는 것을 알려준다
프로그램은 show라는 함수가 호출 되었을 때 호출한 클래스의 객체를 따져서 그에 대응되는 함수를 선택하게 된다.
기초 클래스에서 가상 메소드를 선언하게 된다면 그 함수는 기초 클래스 및 파생 클래스까지 가상함수로 정의가 된다.
객체에 대한 참조 혹은 객체를 지시하는 포인터를 사용하여 가상 메소드가 호출된다면 참조나 포인터를 위해 정의된 함수가 사용되지 않고 그 객체를 위해 정의된 메소드에 사용된다. -> 동적 결합이라고 부른다.
상속을 위해 기초 클래스로 사용할 클래스를 정의할 때 파생 클래스에서 다시 정의해야 되는 클래스 함수들은 가상함수로 선언해야한다.
여러개의 Time 객체와 NewTime객체를 혼합하여 관리를 하고 싶을 때 하나의 배열로 관리하면 효과적이겠지만 이는 불가능하다.
배열이란 저장되는 원소들의 데이터형이 모두 같아야 하는데 두 객체는 엄연히 다른 데이터 형이기 때문에 배열에 저장할 수 없다.
-> 하지만 Time을 지시하는 포인터들의 배열은 만들 수 있다.
-> Time과 NewTime은 public 상속 모델이 적용되어 있고, Time을 지시하는 포인터가 Time객체를 지시할 수도 있고, NewTime객체도 지시할 수 있기 때문이다.
그래서 Time을 지시하는 포인터들의 배열을 만든다면 같은 데이터형으로써 혼합된 배열을 만들 수 있다.
const int MAX = 3;
int main(){
Time* times[MAX];
int day;
int hours;
int mins;
//반복문을 통해 정보를 받아오기
for(int i=0; i< MAX; i++){
cout << i+1 << "번째 원소를 정의합니다." << endl;
cout << "시간을 입력하세요" << endl;
cin >> hours;
cout << "분을 입력하세요" << endl;
cin >> mins;
char check;
cout << "일 정보가 있다면 1, 없다면 0을 입력하세요." << endl;
cin >> check;
if(check=='0')
times[i] = new Time(hours,mins);
else{
cout << "일을 입력하세요" << endl;
cin >> day;
times[i] = new Time(hours,mins, day);
}
}
//출력 반복문
for(int i=0; i< MAX; i++){
cout << i+1 <<"번 째 원소의 정보" << endl;
time[i] -> show();
//객체에 의해 호출되지 않고 참조나 포인터에 의해 호출되었을때 대응되는 show함수가 동작
}
for(int i=0; i< MAX; i++){
delete times[i]; // 동적 할당 했기 떄문에 delete 반드시 해주기
}
}
times의 배열은 time에 대한 포인터 배열이지만 이 배열의 원소에 NewTime이 저장되어 있는 경우에는 NewTime에 대응되는 show가 동작한 것을 확인할 수 있다.
'개념 > c++' 카테고리의 다른 글
vector (1) | 2023.11.30 |
---|---|
C++ 스트림 추출 연산자 오버로딩 (0) | 2023.11.18 |
C++ friend (0) | 2023.11.18 |
C++ 연산자 오버로딩 (1) | 2023.11.18 |
C++ 클래스 객체를 배열로 선언 (1) | 2023.11.18 |