[C++] 구조체/생성자와 소멸자/동적할당/동적배열/상속/가상함수

2022. 6. 13. 19:48돌다리도 두드려보고 건너라 <복만살>

728x90
/* 학습목차
1. 구조체
2. 생성자와 소멸자
3. 동적할당
4. 동적배열
5. 상속
6. 가상함수
*/
#include <iostream>
#include <string>

using namespace std;


// 구조체
// 직접 만드는 쓰는 서로다른 데이터타입의 집합체
// 기능(멤버함수)과 속성(멤버변수)을 지닌 사용자 정의의 데이터타입
struct Monster
{
	string name;
	int hp;
	int atk;

	void SetInfo(string _name, int _hp, int _atk)
	{
		name = _name;
		hp = _hp;
		atk = _atk;
	}

	void ShowInfo()
	{
		cout << name << endl;
		cout << hp << endl;
		cout << atk << endl;
	}
	void Attack()
	{

	}
	void Hit()
	{

	}
	void Die()
	{

	}
};
void main()
{
	Monster monsterA;
	Monster* monsterAPtr;

	monsterA.SetInfo("몬스터A", 100, 10);
	monsterAPtr = &monsterA;
	monsterAPtr->name = "템프";

	monsterA.ShowInfo();
}


// 생성자와 소멸자
// 생성자 : 메모리에 할당이 될때 호출된다, 초기화의 기능이 있다
// 생성자는 기본생성자와 복사생성자가 있다
// 소멸자 : 메모리에서 해제될때 호출된다
struct Monster
{
	string name;
	int hp;
	int atk;

	// 생성자 문법 : 구조체이름()
	Monster()
	{
		cout << "생성자 호출" << endl;
	}
	
	// 생성자 오버로딩
	Monster(string _name, int _hp, int _atk)
	{
		SetInfo(_name, _hp, _atk);
		cout << name << "생성자 호출" << endl;
	}

	// 소멸자 문법 : ~구조체이름()
	~Monster()
	{
		cout << name << "소멸자 호출" << endl;
	}

	void SetInfo(string _name, int _hp, int _atk)
	{
		name = _name;
		hp = _hp;
		atk = _atk;
	}
	void ShowInfo()
	{
		cout << name << endl;
		cout << hp << endl;
		cout << atk << endl;
	}
};

Monster monsterC("C", 100, 10);	// 전역변수 - 데이터영역

void main()
{
	Monster monsterA("A", 100, 10); // 매개변수, 지역변수 - 스택영역
	Monster monsterB("B", 100, 10);
	// 포인터변수는 주소를 담는 변수일뿐, Monster가 아니다
	// 아래 포인터변수는 주소를 Monster의 형태로 해석할 뿐
	Monster* monsterAPtr;	
}


// 동적할당
// new 데이터타입	동적할당을 하면 데이터타입의 크기만큼 Heap 메모리 영역에 할당하고
//					그 시작주소를 반환한다	(메모리에 할당되니까 생성자 호출됨)
// 동적할당을 했으면 다시 해제를 해줘야 한다(delete) 하지않으면 메모리누수 발생
// 컴파일시점과 런타임시점 중 동적할당은 런타임(실행되고 난 이후)에 해당함
struct Monster
{
	string name;
	int hp;
	int atk;

	Monster()
	{
		cout << "생성자 호출" << endl;
	}

	Monster(string _name, int _hp, int _atk)
	{
		SetInfo(_name, _hp, _atk);
		cout << name << "생성자 호출" << endl;
	}

	~Monster()
	{
		cout << name << "소멸자 호출" << endl;
	}

	void SetInfo(string _name, int _hp, int _atk)
	{
		name = _name;
		hp = _hp;
		atk = _atk;
	}
	void ShowInfo()
	{
		cout << name << endl;
		cout << hp << endl;
		cout << atk << endl;
	}
};

Monster monsterC("C", 100, 10);	

void main()
{
	int num = 10;
	int* numPtr = new int;
	*numPtr = 30;

	cout << *numPtr << endl;
	delete numPtr;

	Monster monsterA("A", 100, 10);
	Monster monsterB("B", 100, 10);

	Monster* monsterAPtr = new Monster("D", 100, 10);
	// 메모리 누수 발생 - 동적할당은 delete를 해야한다
	delete monsterAPtr;
}


// 동적배열
struct Monster
{
	string name;
	int hp;
	int atk;

	Monster()
	{
		cout << "생성자 호출" << endl;
	}

	Monster(string _name, int _hp, int _atk)
	{
		SetInfo(_name, _hp, _atk);
		cout << name << "생성자 호출" << endl;
	}

	~Monster()
	{
		cout << name << "소멸자 호출" << endl;
	}

	void SetInfo(string _name, int _hp, int _atk)
	{
		name = _name;
		hp = _hp;
		atk = _atk;
	}
	void ShowInfo()
	{
		cout << name << endl;
		cout << hp << endl;
		cout << atk << endl;
	}
};

// 동적할당을 통해 주소를 넘겨주어야 한다. textrpg만들때 자주 쓰임(예를 들면 캐릭터생성시)
// 따로 해제를 해주지 않은 이상 주소가 남기때문이다.
// 지역변수를 통해 넘기려고 하면, 지역변수는 괄호가 끝나면서 할당이 해제되기때문
// 후에 사용할 때 문제가 생길 수 있다
// Monster temp;
// temp.SenInfo(name,hp,atk);
// return &temp;

// 따라서 함수 내에서 생성한 객체를 넘기고 싶다면 동적할당을 통해 넘겨준다.
Monster* CreateMonster()
{
	string name;
	cout << "생성할 몬스터의 이름을 입력하시오 : ";
	cin >> name;

	int hp;
	cout << "생성할 몬스터의 체력을 입력하시오 : ";
	cin >> hp;

	int atk;
	cout << "생성할 몬스터의 공격력을 입력하시오 : ";
	cin >> atk;

	return new Monster(name, hp, atk);
}

void main()
{
	Monster* monsters[3];
	monsters[0] = new Monster("A", 100, 10);
	monsters[1] = new Monster("B", 150, 10);
	monsters[2] = new Monster("C", 200, 10);

	Monster* createMonster;

	createMonster = CreateMonster();
	createMonster->ShowInfo();
	delete createMonster;

	for (int i = 0; i < 3; i++)
	{
		monsters[i]->ShowInfo();
		delete[] monsters[i];
	}
}


// 상속
// 상속을 하는 2가지 경우는
// ①중복이 되서 합치는 경우 ②같은 곳에서 관리하기 위해서 하는 경우가 있다. 
// 공통분모를 하나로 추상화(통합)하는 과정

// 부모 구조체
struct SchoolMember
{
	string name;
	int id;

	void SetInfo(string _name, int _id)
	{
		name = _name;
		id = _id;
	}
	void ShowInfo()
	{
		cout << "교내 멤버정보" << endl;
		cout << name <<	id << endl;
	}
};

// 상속 : 부모가 자식에게 속성과 기능, 그리고 정체성을 물려주는 것
// [문법]상속받을 구조체명 : 상속할 구조체명
//			is a 관계	= 선생은 학교멤버이다
struct Teacher : SchoolMember
{
	string subject;

	// 함수의 오버라이딩 : 재정의
	// 부모의 함수를 자식에서 재정의하는 것
	void SetInfo(string _name, int _id, string _subject)
	{
		SchoolMember::SetInfo(_name, _id);
		subject = _subject;
	}
	void ShowInfo()
	{
		cout << "선생정보" << endl;
		cout << name << id << subject << endl;
	}

	void Teach()
	{

	}
};
//			is a 관계	= 학생은 학교멤버이다
struct Student : SchoolMember
{
	int grade;

	// 함수의 오버라이딩 : 재정의
	void SetInfo(string _name, int _id, int _grade) 
	{
		name = _name;
		id = _id;
		grade = _grade;
	}
	void ShowInfo()
	{
		cout << "학생정보" << endl;
		cout << name << id << grade << endl;
	}
	void Study()
	{

	}
};

void main()
{
	Student studentA;
	studentA.SetInfo("학생A", 1, 4);

	Teacher teacherA;
	teacherA.SetInfo("선생A", 1, "과학");

	studentA.ShowInfo();
	teacherA.ShowInfo();

	SchoolMember tempMember;

	// 선생과 학생이 학교의 멤버이기때문에 들어갈 수 있다
	// 복사되어 들어갈때, SchoolMember의 형태에 맞도록 짤려서 복사되기 때문에 Student의 기능들 또한 짤리게 된다
	tempMember = studentA;		// 된다
	cout << "현재 임시 멤버는 : ";
	tempMember.ShowInfo();

	tempMember = teacherA;		// 된다
	cout << "현재 임시 멤버는 : ";
	tempMember.ShowInfo();
	
}


// 가상함수
// 부모의 함수에 virtual를 붙인것
// 함수가 호출 될 때, 자식에서 재정의가 되었는지 확인하고 재정의가 되었다면 자식의 함수를 호출함
struct SchoolMember
{
	string name;
	int id;

	SchoolMember()
	{

	}

	SchoolMember(string _name, int _id)
	{
		name = _name;
		id = _id;
	}

	void SetInfo(string _name, int _id)
	{
		name = _name;
		id = _id;
	}
	void virtual ShowInfo()
	{
		cout << "교내 멤버정보" << endl;
		cout << name << id << endl;
	}

	// 가상소멸자
	virtual ~SchoolMember()
	{
		cout << name << "교내멤버소멸자" << endl;
	}
};

struct Teacher : SchoolMember
{
	string subject;

	Teacher()
	{

	}

	Teacher(string _name, int _id, string _subject) : SchoolMember(_name, _id)
	{
		subject = _subject;
	}

	~Teacher()
	{
		cout << name << "선생소멸자" << endl;
	}

	void SetInfo(string _name, int _id, string _subject)
	{
		SchoolMember::SetInfo(_name, _id);
		subject = _subject;
	}
	void ShowInfo()
	{
		cout << "선생정보" << endl;
		cout << name << id << subject << endl;
	}

	void Teach()
	{

	}
};

struct Student : SchoolMember
{
	int grade;

	Student()
	{

	}

	Student(string _name, int _id, int _grade) : SchoolMember(_name, _id)
	{
		grade = _grade;
	}

	~Student()
	{
		cout << name << "학생소멸자" << endl;
	}

	void SetInfo(string _name, int _id, int _grade)
	{
		name = _name;
		id = _id;
		grade = _grade;
	}
	void ShowInfo()
	{
		cout << "학생정보" << endl;
		cout << name << id << grade << endl;
	}
	void Study()
	{

	}
};

void main()
{
	SchoolMember* schoolMembers[4];

	schoolMembers[0] = new Student("학생A", 0, 4);	// 업케스팅
	schoolMembers[1] = new Student("학생B", 0, 3);
	schoolMembers[2] = new Student("학생C", 0, 2);
	schoolMembers[3] = new Teacher("선생A", 0, "물리");

	for (int i = 0; i < 4; i++)
	{
		schoolMembers[i]->ShowInfo();
	}

	// 가상 소멸자 생성
	for (int i = 0; i < 4; i++)
	{
		delete schoolMembers[i];
	}

	/*
	Teacher teacherA;
	teacherA.SetInfo("선생", 1, "과학");
	
	// 같은 주소를 가르키더라도 포인터변수 데이터타입의 형태에 따라 해석의 범위가 달라진다
	Teacher* teacherPtr;
	teacherPtr = &teacherA;
	teacherPtr->ShowInfo();

	SchoolMember* schoolMemberPtr;
	schoolMemberPtr = &teacherA;	// 업케스팅 : 자식에서 부모로 올라가는 것
	// 부모의 형태->ShowInfo()를 호출할때 ShowInfo가 virtual함수이므로 자식에서 재정의가 되었는지 확인
	// 자식 Teacher에는 재정의가 되어있기 때문에 재정의 되어있는 ShowInfo() 즉, 선생정보출력을 호출함
	schoolMemberPtr->ShowInfo();	// 다운캐스팅 : 부모에서 자식으로 내려가는 것

	// Teacher의 형태로 형변환
	((Teacher*)schoolMemberPtr)->ShowInfo();
	*/
}

구조체 출력결과
생성자 소멸자 출력결과
동적할당 출력결과
동적배열 출력결과
상속 출력결과
가상함수, 가상소멸자 출력결과

728x90