C언어 공부

C++ 기초 - 3 소멸자 / 연습문제

당혜성 2025. 3. 15. 20:50

this

this 키워드

클래스 멤버 함수는 this라는 키워드를 통해 자기 자신을 인스턴스(포인터 타입)를 가져올 수 있다.

 

클래스 멤버 함수를 호출하기 위해서는 호출한 주체인 어떤 클래스 인스턴스가 존재하게 되는데 그 호출 주체를 this라고 한다.

 

예시코드

class student {
public:
	int stdno;
	char name[20];
	int gender; // 0 = 남자 1 = 여자
	
	student(int stdno, const char* n, int g) {
		this->stdno = stdno;
		strcpy(name, n);
		gender = g;
	}

	void printstudent() {
		printf("학생 번호%d\n", stdno);
		printf("학생 이름:%s\n", name);
		printf("성별 %s\n", gender == 0 ? "남자" : "여자");
	}
};

this-> 를 쓰고 this->stdno에 있는 stdno에 커서를 올려보면 = stdno;가 되있는 부분은 파란불이 안들어올 것이다.

 

인스턴스(Instance)

클래스는 정의만으로는 존재할 수 없다. 그냥 초안일 뿐이다. 우리가 여지껏 '클래스의 변수' 라고 말해왔던 것을 클래스 인스턴스라고 생각하면 편하다.

 

이 두개도 인스턴스다

student s = student <- 함수 영역에 있는 인스턴스

 

student* ps = new student <- Heap 영역에 있는 인스턴스

 

소멸자(Destructor)

클래스는 소멸자(Destructor)를 통해 메모리에서 해제될 때의 행동을 지정해줄 수 있다. 이를 소멸자라고 한다.

 

소멸자는 ~클래스명() 으로 선언할 수 있으며, 소멸자도 리턴타입이 존재하지 않는다.

 

예시 코드

class student {
public:
	int stdno;
	char name[20];
	int gender; // 0 = 남자 1 = 여자
	
	student(int stdno, const char* n, int g) {
		this->stdno = stdno;
		strcpy(name, n);
		gender = g;
	}

	~student() {

	}

	void printstudent() {
		printf("학생 번호%d\n", stdno);
		printf("학생 이름:%s\n", name);
		printf("성별 %s\n", gender == 0 ? "남자" : "여자");
	}
};

정적 할당된 함수에서는 소멸자가 호출 될 것이다.

 

예시 코드

#include "Header.h"

class student {
public:
	int stdno;
	char name[20];
	int gender; // 0 = 남자 1 = 여자
	
	student(int stdno, const char* n, int g) {
		this->stdno = stdno;
		strcpy(name, n);
		gender = g;
	}

	~student() {
		printf("%s의 소멸자 호출", name);
	}

	void printstudent() {
		printf("학생 번호%d\n", stdno);
		printf("학생 이름:%s\n", name);
		printf("성별 %s\n", gender == 0 ? "남자" : "여자");
	}
};

int main() {
	student s = student(1234, "이혜성", 0);
	s.printstudent();

	/*student* ps = new student(3456, "당혜성", 1);

	ps->printstudent();*/

	
	return 0;
}

이러면 소멸자 호출이 나올것이다.

 

반대로 동적으로 할당된 함수에서는 호출이 안될것이다.

코드

#include "Header.h"

class student {
public:
	int stdno;
	char name[20];
	int gender; // 0 = 남자 1 = 여자
	
	student(int stdno, const char* n, int g) {
		this->stdno = stdno;
		strcpy(name, n);
		gender = g;
	}

	~student() {
		printf("%s의 소멸자 호출", name);
	}

	void printstudent() {
		printf("학생 번호%d\n", stdno);
		printf("학생 이름:%s\n", name);
		printf("성별 %s\n", gender == 0 ? "남자" : "여자");
	}
};

int main() {
	/*student s = student(1234, "이혜성", 0);
	s.printstudent();*/

	student* ps = new student(3456, "당혜성", 1);
	ps->printstudent();

	
	return 0;
}

이 뜻은 정적으로 할당된 함수에서는 제대로 삭제가 됐다는 뜻인데, 동적으로 할당된 함수는 제대로 삭제가 안되었단 뜻이다. 그래서 delete 라는 키워드를 사용한다.

 

예시 코드

int main() {
	/*student s = student(1234, "이혜성", 0);
	s.printstudent();*/

	student* ps = new student(3456, "당혜성", 1);
	ps->printstudent();
	delete ps;
	
	return 0;
}

 

소멸자 호출의 필요성

모든 동적 메모리를 수동으로 관리해줘야 하는 c++의 특성상 클래스 멤버 변수에 동적 메모리 할당된 개체가 있다고 할 경우, 소멸자에서 처리하지 않으면 하나하나 클래스 외부에 delete 전에 메모리를 해제해야 한다.

 

이는 굉장히 불편함과 불합리함을 초래하게 되는데, 이를 해결할 수 있는 것이 소멸자이다.

 

예를 들어보자.

지금 우리가 char name[20]; 정적으로 20개인 배열을 만들었는데,

 

만약에 우리가 char* name; 동적인 형태로 만든다고 생각해보자.

예시 코드

int stdno;
char* name;
int gender; // 0 = 남자 1 = 여자

student(int stdno, const char* n, int g) {
	this->stdno = stdno;
	strcpy(name, n);
	gender = g;
}

이렇게 하면 strcpy를 사용할 수없다. 메모리를 초기화 해보자.

 

name = new char[20]; 이렇게 해도 낭비되는 공간이 있어서 싫을 수 있다.

 

name = new char[strlen(n) + 1]; strlen은 null 문자를 제외한 길이를 나타내지만
+1 까지 해서 null 문자까지 센다.

 

그럼 문자열과 딱 맞는 배열로 만들어져서 호출하게 된다.

 

그런데 동적 할당이 되어있기 때문에 해제를 해야한다.

 

근데 우리가 해제하는 시점을 잘 잡을 수 없을 뿐더러, 대부분의 사람들은 못잡는다.

 

~student() {
	printf("%s의 소멸자 호출", name);
	delete[] name;
}

그래서 이렇게 하는게 낫다.

 

연습

어느 회사는 사원 관리 시스템이 있다. 

이 출근관리 시스템은 관리자에 의해 사원을 입력받아 등록할 수 있으며, 사원은 각각 사원번호, 이름, 성별, 직급 이라는 속성을 가지고 있다.

관리자는 모든 사원을 한번에 볼 수 있고, 사원은 퇴사시 관리자에 의해 삭제될 수 있다.

삭제는 사원번호를 입력받아 퇴사처리를 할 수 있따.

 

사원번호는 시스템에 의해 자동으로 관리되며, 사원번호가 관리되는 방법은 자유롭게 구현할 수 있다.

 

나는 이거 어려워서 따라 쳤다

 

헤더코드

#define _CRT_SECURE_NO_WARNINGS
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cstring>
#include <cstdio>

int number_counter = 1;

class employee {
public:
	int no;
	char name[20];
	int gender;
	char rank[20];

	employee(char* name, int gender, char* rank) {
		strcpy(this->name, name);
		this->gender = gender;
		strcpy(this->rank, rank);
		this->no = number_counter;
		number_counter++;
	}

	void printinfo() {
		printf("사원 번호:%d\n",no);
		printf("사원 이름:%s\n", name);
		printf("사원 성별:%s\n", gender == 1 ? "남성" : "여성");
		printf("사원 직급:%s\n", rank);
	}
};

int getint(const char* prompt);


char* getString(const char* prompt);

 

소스코드

#define _CRT_SECURE_NO_WARNINGS
#include "mainHeader.h"


int main() {
	employee* emp[100];
	int count = 0;

	while (true) {
		printf("명령 입력\n");
		printf("1. 사원 보기\n");
		printf("2. 사원 추가\n");
		printf("3. 사원 삭제\n");
		printf("4. 프로그램 종료\n");

		fseek(stdin, 0, SEEK_END);
		int input;
		scanf("%d", &input);

		if (input == 1) {
			for (int i = 0; i < count; i++) {
				emp[i]->printinfo();
			}
		}
		else if (input == 2) {
			char* name= getString("사원명 입력:\n");
			int gender = getint("성별 입력(1. 남성 2. 여성)");
			char* rank = getString("직급을 입력:");

			employee* e = new employee(name, gender, rank);
			delete[] name;
			delete[] rank;

			emp[count] = e;
			count++;
		}
		else if (input == 3) {
			int number = getint("사원 번호 입력:\n");
			int deletedinedex = -1;
			for (int i = 0; i < count; i++) {
				if (number == emp[i]->no) {
					delete emp[i];
					deletedinedex = i;
					break;
				}
			}
			if (deletedinedex >= 0) {
				for (int i = deletedinedex; i < count; i++) {
					emp[i] = emp[i + 1];
				}
			}
			count--;
			
			printf("삭제 완료");
		}
		else if (input == 4) {
			printf("프로그램 종료\n");
			break;
		}
		else {
			printf("올바르지 않은 입력\n");
		}
	}


	return 0;
}

int getint(const char* prompt){
	int input;
	printf("%s", prompt);
	fseek(stdin, 0, SEEK_END);
	scanf("%d", &input);
	return input;
}

char* getString(const char* prompt) {
	char* input = new char[100];
	printf("%s", prompt);
	fseek(stdin, 0, SEEK_END);
	scanf("%99[^\n]s", input);
	return input;
}