C/C++ - 상속성

Claire·2024년 8월 29일

상속의 구조

객체 지향에서의 상속성

  • 클래스란 사용자가 정의한 하나의 데이터 타입이다.
  • 완성된 하나의 데이터타입인 클래스를 다른 클래스에 상속함으로써 기능을 제공할 수 있는데 이것이 "객체지향에서의 상속성"이다.

클래스의 상속 구조

클래스간 상속 구조 형태

regular 클래스는 employee 클래스의 이름, 주소에 대한 멤버 변수들을 상속받음

클래스간의 상속 구조 계층도

이러한 부모 클래스와 자식 클래스 간의 관계를 '상속성'이라고 한다.

상속의 특징

  1. 부모속성은 누가 상속을 받아갔는지 알 수가 없다.
  2. friend 속성은 상속이 불가능하다. (부모 속성이 다른 클래스를 frined로 했다고 자식 속성이 부모 속성의 friend 값에 접근은 불가능)

상속관계 특징

객체 생성 순서
객체 소멸 순서

상속 클래스의 객체 생성 및 소멸 코드는 아래 코드를 참고

#include<iostream>

using namespace std;

class Staff {
public:
	Staff() {
		this->strName = NULL;
		this->strAddr = NULL;
		cout << "1. Staff 디폴트 생성자 호출" << endl;
	}

	Staff(const char* pName, const char*pAddr) {
		cout << "1. Staff 전달인자 2개와 생성자 호출" << endl;
		this->strName = new char[strlen(pName)+1];
		this->strAddr = new char[strlen(pAddr) + 1];
		strcpy_s(this->strName, strlen(pName) + 1, pName);
		strcpy_s(this->strAddr, strlen(pAddr) + 1, pAddr);
	}

	~Staff() {
		delete[] strName;
		delete[] strAddr;
		cout << "1. Staff 소멸자 호출" << endl;
	}

protected:
	char* strName;
	char* strAddr;
};

class Permanent : Staff {
public: 
	Permanent(const char* pName, const char* pAddr, double dSalary) {
		cout << "2. Permanent 인자가 3개인 생성자 호출" << endl;
		this->strName = new char[strlen(pName) + 1];
		this->strAddr = new char[strlen(pAddr) + 1];
		strcpy_s(this->strName, strlen(pName) + 1, pName);
		strcpy_s(this->strAddr, strlen(pAddr) + 1, pAddr);
		salary = dSalary;

	}
	~Permanent() {
		cout << "2. Permanent 소멸자 호출" << endl;
	}

	double PayCheck() const {
		return salary;
	}

private:
	double salary;
};

class Temporary : Staff {
public:
	Temporary(const char* pName, const char* pAddr, double dDailyPayCheck, int nDays) : Staff(pName, pAddr) {
		cout << "3. Temporary 인자가 4개인 생성자 호출 " << endl;
		dailyPayCheck = dDailyPayCheck;
		days = nDays;
	}

	~Temporary() {
		cout << "3. Temporary 소멸자 호출 " << endl;
	};

	double PayCheck() const {
		return dailyPayCheck * days;
	}

private:
	double dailyPayCheck;
	int days;
};

void main() {
	Permanent rgl("kim", "suwon", 300);
	Temporary tmp("park", "seoul", 10, 20);
	cout << rgl.PayCheck() << endl;
	cout << tmp.PayCheck() << endl;
}

상기 코드에서 자식 속성에 ": 부모속성(매개변수)"을 붙여주게 되면 부모 클래스에서 어떤 생성자를 호출해서 상속 받을지를 명확히 할 수 있다.

출력 결과 값

부모 클래스의 멤버 접근

클래스 멤버 변수로 사용 시

  • protected: 데이터 외부에서 접근할 수 없고 상속 받은 자식 클래서에서만 접근이 가능
  • protected는 외부에서 바라보았을 때, private과 같은 속성이지만, 상속받은 자식 클래스 입장에서는 public과 같은 속성을 가지고 있다.

멤버 함수의 오버라이딩

오버라이딩

사전적 의미로 '~위에 덮어쓰다' 또는 '~에 우선한다'는 의미
즉, 부모 클래스에서 정의한 멤버 함수를 자식 클래스가 그대로 상속받되, 내용을 변경하여 새로운 기능을 만드는 것

멤버 함수 오버라이딩 조건
1. 이름이 같아야 한다.
2. 매개변수의 타입 및 개수가 닽아야 한다.
3. 반환 타입이 같아야 한다.

이 말인 즉슨, 부모와 자식의 함수가 동일해야 한다는 것을 의미한다.
코드로 예시를 들어 보면

class Staff {
public:
	Staff() {
		this->strName = NULL;
		this->strAddr = NULL;
	}
	Staff(const char* pName, const char*pAddr) {
		this->strName = new char[strlen(pName)+1];
		this->strAddr = new char[strlen(pAddr) + 1];
		strcpy_s(this->strName, strlen(pName) + 1, pName);
		strcpy_s(this->strAddr, strlen(pAddr) + 1, pAddr);
	}
	~Staff() {
		delete[] strName;
		delete[] strAddr;
	}
	double PayCheck() const {
		return 0.0;
	}

protected:
	char* strName;
	char* strAddr;
};

class Permanent : Staff {
public: 
	Permanent() {}
	Permanent(const char* pName, const char* pAddr, double dSalary) : Staff(pName, pAddr) {
		salary = dSalary;

	}
	~Permanent() {
	}

	double PayCheck() const {
		return salary;
	}

private:
	double salary;
};
class SalesMan : Permanent {
public:
	SalesMan() {
	}
	
	SalesMan(const char* pName, const char* pAddr, double dSalary, double dAllowance) : Permanent(pName, pAddr, dSalary) {
        allowance = dAllowance
	}

	~SalesMan() {
	}

	double PayCheck() const {
		return Permanent::PayCheck() + allowance;
	}

private:
	double allowance;
};

void main() {
	SalesMan slm("jung", "boondang", 300, 50);
	cout << "급여: " << slm.PayCheck() << endl;
}

위 코드를 살펴보면 Staff 클래스가 Permanent에 이름과 주소 그리고 PayCheck() 함수를 내려주고 해당 값을 상속받은 Permanent가 다시 SalesMan에 Salary값을 포함하여 다시 내려주고 있다.
따라서, 메인에서 호출한 SalesMan 클래스의 PayCheck가 Permanent의 PayCheck를 오버라이딩해서 Permanent의 PayCheck 함수의 기능에 '+ allowance' 한 값인 "350"을 결과값으로 return하는 것이다.

부모클래스와 자식 클래스 객체 포인터 접근

  • 부모 클래스 객체 포인터는 자식 클래스의 객체 접근이 가능
  • 부모 클래스 객체 포인터를 통해 자식 클래스 객체 포인터를 제어
Staff* emp = (Staff*)&rgl;
emp->PayCheck();
cout << emp->PayCheck() << endl;

우리가 배운 대로라면 위 코드에서 emp가 호출하는 PayCheck( )는 누구의 함수가될까??
나는 Permanent의 PayCheck( ) 함수가 호출될 것이라고 예상한다.
emp는 어디까지나 Staff 타입을 가져올 뿐 직접적으로 생성하고 있는 객체는 Permanent이기 때문에 해당 객체를 생성하면서 Staff의 함수는 오버라이딩 되었을 것이라고 예상하기 때문이다.

profile
SEO 최적화 마크업 개발자입니다.

0개의 댓글