본문 바로가기

Programming/C++ 3

[C++] 07-02. 상속의 문법적인 이해

반응형

상속의 표현

A 아버지 <- B 자식

 

상속의 원리

상속한 객체 안에는 B클래스의 멤버 뿐 아니라 A클래스의 멤버도 존재한다.

->B클래스의 멤버함수 내에서는 A클래스의 멤버함수나 멤버변수를 호출 가능

 

 

임의의 클래스 생성자 내에서는 임의의 클래스 내의 멤버를 모두 초기화 해야한다.

-> 생성자의 존재 목적

 

생성자에서 동적할당 할 경우 소멸자에서 해제를 해야한다.

-> 임의의 클래스 안에 있는 멤버를 다른 클래스 생성자에서 초기화 불가

 

A클래스의 멤버변수는 어디에서 초기화하는게 올바른가?

-> 상속을 했다고 하더라도 원칙이 깨져서는 안된다.

 

B클래스의 객체를 동적할당으로 생성

-> B클래스의 생성자만 호출하지만 A클래스의 멤버변수도 초기화가 되어야 한다. [같이 인자로 전달]

 

객체는 하나이지만, 두 개의 생성자를 호출

-> B클래스 내 생성자는 B클래스의 멤버변수를 초기화하고

-> A클래스 내 생성자는 A클래스의 멤버변수를 초기화한다.

 

B클래스의 생성자만 호출하고, 명시적으로 A클래스의 생성자 호출을 명령한다.


Person

상위 클래스, 기초 클래스, 슈퍼 클래스, 부모 클래스

 

UnivStudent

하위 클래스, 유도 클래스, 서브 클래스, 자식 클래스


A클래스의 멤버는 A클래스에서 초기화한다.

  • 이니셜라이저로 초기화
  • 생성자의 몸체를 통해서 초기화

 

 

DerivedClassName(BaseClass의 인자, DerivedClass의 인자) : Base클래스 / Derived 클래스 순으로 작성

 

 

 

B클래스는 A클래스의 멤버를 직접 초기화하진 않지만

  • A클래스의 멤버를 초기화할 데이터를 전달한다
  • 전달할 인자를 A클래스의 생성자를 호출하면서 전달한다.

 

 

//상속의 방법과 그 결과
#include<iostream>
#include<cstring>
using namespace std;

class Person
{
private:
	int age;
	char name[50];
public:
	Person(int myage, const char* myname) : age(myage) //age는 이니셜라이저를 이용해 초기화
	{
		strcpy_s(name,strlen(myname)+1, myname); //name은 생성자의 몸체를 통해 초기화
	}	//A클래스의 멤버는 A클래스에서 초기화
	void WhatYourName() const
	{
		cout << "My name is " << name << endl;
	}
	void HowOldAreYou() const
	{
		cout << "I'm " << age << " years old" << endl;
	}
};

class UnivStudent : public Person //상속의 방법 [public 상속]
{
private:
	char major[50];
public:
	UnivStudent(const char* myname, int myage, const char* mymajor) 
		:Person(myage, myname)
	//Person 클래스의 멤버 직접 초기화는 하지않고,
	//Person클래스의 인자로 전달받고 생성자 호출 명령으로 인자로 전달 한다.
	{
		strcpy_s(major,strlen(mymajor)+1, mymajor);
	}
	void WhoAreYou() const
	{
		WhatYourName();
		HowOldAreYou(); //Person 클래스의 멤버함수 호출
		cout << "My major is " << major << endl << endl;
	}
};
int main(void)
{
	UnivStudent ustd1("Lee", 22, "Computer eng."); //기초 클래스의 생성자 호출을 위한 인자까지 전달
	ustd1.WhoAreYou();
	UnivStudent ustd2("Yoon", 21, "Electronic eng."); 
	//private 멤버는 유도클래스에서 접근 불가능하므로 생성자의 호출을 통해서 기초클래스 멤버 초기화
	ustd2.WhoAreYou();
	
	return 0;
}

 

 


유도 클래스의 객체생성 과정 Case1

SoDerived dr3(23,24)

1단계. 메모리 공간의 할당

2단계. 유도 클래스의 생성자 호출

	SoDerived(int n1, int n2) : SoBase(n1), derivNum(n2)//2단계. 유도 클래스의 생성자 호출
		//n2는 deriveNum초기화, n1은 상속한 SoBase호출 인자전달
	{
		cout << "SoDerived(int n1, int n2)" << endl; //4단계. 유도 클래스의 생성자 실행
	}

n1과 n2에 인자를 전달하고, 이니셜라이저 처리 후, 몸체 실행

 

3단계. 기초 클래스의 생성자 호출 및 실행

4단계. 유도 클래스의 생성자 실행

 

 

유도 클래스의 객체생성 과정 Case2

SoDerived dr1

1단계. 메모리 공간의 할당

2단계. 유도 클래스의 void 생성자 호출

3단계. 이니셜라이저를 통한 기초 클래스의 생성자 호출이 명시적으로 정의되어 있지 않으므로 void 생성자 호출

4단계. 유도 클래스의 생성자 실행

 


//유도클래스의 객체 생성 과정
#include <iostream>
using namespace std;

class SoBase
{
private:
	int baseNum;
public:
	SoBase() : baseNum(20) //이 생성자가 없다면 에러 발생 -> 객체생성이 불가능
	{
		cout << "Sobase()" << endl;
	}
	SoBase(int n) : baseNum(n)
	{
		cout << "Sobase(int n)" << endl;
	}
	void ShowBaseData()
	{
		cout << baseNum << endl;
	}
};

class SoDerived :public SoBase
{
private:
	int derivNum;
public:
	
	SoDerived() : derivNum(30) //상속하고 있는 기초클래스의 생성자 명령이 없다
							   //인자를 받지않는 void 생성자 호출
	{
		cout << "SoDerived()" << endl;
	}
	SoDerived(int n) : derivNum(n) //인자를 받지않는 void 생성자 호출
	{
		cout << "SoDerived(int n)" << endl;
	}
	SoDerived(int n1, int n2) : SoBase(n1), derivNum(n2)
		//n2는 deriveNum초기화, n1은 상속한 SoBase호출 인자전달
	{
		cout << "SoDerived(int n1, int n2)" << endl; 
	}
	void ShowDerivData()
	{
		ShowBaseData();
		cout << derivNum << endl;
	}
};

int main(void)
{
	
	cout << "case1..." << endl;
	SoDerived dr1;
	dr1.ShowDerivData();
	cout << "---------------" << endl;
	cout << "case2..." << endl;
	SoDerived dr2(12);
	dr2.ShowDerivData();
	cout << "---------------" << endl;
	cout << "case3..." << endl;
	SoDerived dr3(23,24);
	dr3.ShowDerivData();
	cout << "---------------" << endl;
	return 0;
}


유도 클래스 객체의 소멸 과정

유도 클래스의 소멸자가 실행된 이후에 기초 클래스의 소멸자가 실행된다.

 

스택에 생성된 객체의 소멸 순서는 생성순서와 반대이다.


 

//유도 클래스 객체의 소멸 과정
#include <iostream>
using namespace std;

class SoBase
{
private:
	int baseNum;
public:
	SoBase(int n) :baseNum(n)
	{
		cout << "SoBase(): " << baseNum << endl;
	}
	~SoBase()
	{
		cout << "~SoBase(): " << baseNum << endl;
	}
};

class SoDerived :public SoBase
{
private:
	int derivNum;
public:
	SoDerived(int n) :SoBase(n), derivNum(n)
	{
		cout << "SoDerived(): " << derivNum << endl;
	}
	~SoDerived()
	{
		cout << "~SoDerived(): " << derivNum << endl;
	}
};
int main_4(void)
{
	SoDerived drv1(15);
	SoDerived drv2(27);
	return 0;
}

 

 

반응형