프로그래밍/C++
상속이란
revivekirin
2024. 3. 27. 14:13
- 파생 클래스의 인스턴스가 생성될 때 기본 클래스의 생성자도 호출된다
- 파생 클래스는 기본 클래스의 멤버에 접근할 수 있다. 단, private 접근 제어 지시자로 선언된 클래스 멤버에는 접근할 수 없다.
- 사용자 코드에서는 파생 클래스의 인스턴스를 통해 기본 클래스 메서드를 호출할 수 있다.
#include <iostream>
using namespace std;
class CMyData
{
public:
CMyData() {cout<<"CMyData"<<endl;}
int GetData() {return m_nData;}
void SetData(int nParam) {m_nData = nParam;}
protected: //파생클래스만 접근 가능
void printData() {cout<<"CMyData::PrintData()"<<endl;}
private:
int m_nData=0;
};
class CMyDataEx : public CMyData
{
public:
CMyDataEx() {cout<<"CMyDataEx()"<<endl;}
void TestFunc()
{
//기본 형식 멤버에 접근
printData();
SetData(5);
cout<<CMyData::GetData()<<endl;
}
};
int main()
{
CMyDataEx data;
//기본 클래스(CMyData)멤버에 접근
data.SetData(10);
cout<<data.GetData()<<endl;
//파생 클래스(CMyDataEx) 멤버에 접근
data.TestFunc();
return 0;
}
매서드 재정의(Override)
메서드를 재정의하면 기존의 것이 '무시'된다.
만일 파생 클래스에서 기본 클래스의 메서드를 재정의하면 기존의 것은 무시되고 새로 정의된 것이 기존 것을 대체한다.
#include <iostream>
using namespace std;
class CMyData
{
public:
int GetData() {return m_nData;}
void SetData(int nParam) {m_nData = nParam;}
private:
int m_nData = 0;
};
class CmyDataEx:public CMyData
{
public:
void SetData(int nParam)
{
if(nParam <0)
CMyData::SetData(0); //SetData(0)으로 표기될 경우 재귀 호출 발생
if(nParam > 10)
CMyData::SetData(10); //SetData(0)으로 표기될 경우 재귀 호출 발생
}
};
int main()
{
//구형에는 값을 보정하는 기능이 없다
CMyData a;
a.SetData(-10);
cout<<a.GetData()<<endl; //출력: -10
//신형에는 값을 보정하는 기능이 있다
CmyDataEx b;
b.SetData(15);
cout<<b.GetData()<<endl; //출력: 10
return 0;
}
참조 형식과 실형식
#include <iostream>
using namespace std;
class CMyData
{
public:
CMyData() {cout<<"CMyData"<<endl;}
int GetData() {return m_nData;}
void SetData(int nParam) {m_nData = nParam;}
protected: //파생클래스만 접근 가능
void printData() {cout<<"CMyData::PrintData()"<<endl;}
private:
int m_nData=0;
};
class CMyDataEx : public CMyData
{
public:
CMyDataEx() {cout<<"CMyDataEx()"<<endl;}
void TestFunc()
{
//기본 형식 멤버에 접근
printData();
SetData(5);
cout<<CMyData::GetData()<<endl;
}
};
int main()
{
CMyDataEx a;
CMyData &rData = a;
rData.SetData(15);
cout<< rData.GetData() <<endl;
return 0;
}
rData.SetData(15)처럼 실 형식과 참조 형식이 서로 다른 경우 묵시적인 형태로 '참조 형식'이 호출된다.
int main()
{
CMyData *pData = new CMyDataEx;
pData-> SetData(5);
delete pData;
return 0;
}
포인터로 참조 형식을 사용할 때는 CMyDataEx에 대한 소멸자가 추가적으로 필요하다.
delete pData를 하더라도 포인터만 삭제되지, CMyDataEx클래스의 소멸자는 호출되지 않기 때문이다.
상속에서의 생성자와 소멸자
생성자는 호출은 실행 순서가 역순이고 소멸자는 같다
이러한 순서로 생성, 소멸될 경우 파생 클래스에서 부모 클래스의 멤버 변수에 접근하여 쓰기 연산하는 것은 논리 오류를 발생시킨다.
때문에 파생 클래스에서는 부모 클래스의 멤버 변수에 직접 쓰기나 초기화를 하지 않아야 한다.
생성자 선택
CMyDataEx() : CMyData()
...
CMyDataEx(int nParam) : CMyData(nParam)
...
CMyDataEx(double nParam) : CMyData(nParam)
class CMyDataEx : public CMyData
{
public
using CMyData::CMyData;
};