공부/C++

[C++] 생성자와 소멸자

코멍이 2023. 4. 2. 16:18

 

생성자와 소멸자

 

<목차>

1. 생성자 (Constructor)
2. 소멸자 (Destructor)
3. 생성자와 소멸자의 실행순서

 

 

1. 생성자 (Constructor)

 

생성자는 객체가 생성되는 시점에서 자동으로 호출되는 멤버함수이다. 객체를 생성할 때 딱 한 번만 실행되며 이것은 우리가 임의로 생성할 수 없다. 또, 생성자의 이름은 반드시 클래스명과 동일해야 하며 리턴타입을 선언하지 않는다. 

 

class Circle{
        Circle(); //생성자
        Circle(int radius); //생성자
};

Circle::Circle(){

}

Circle::Circle(int radius){

}

* 생성자는 한 클래스내에 여러개가 존재할 수 있다. 중복된 생성자는 그 중 하나만 실행된다.

 

 

그렇다면 생성자는 어떤 목적으로 사용할까? 생성자는 객체가 생성될 때 객체가 필요한 값을 초기화 하기 위해 사용된다. (ex. 멤버변수의 값, 메모리 할당, 파일 열기, 네트워크 연결등) 만약 생성자가 선언되지 않으면 컴파일러가 자동으로 생성자를 만들어준다. 이때 만들어지는 생성자는 매개변수가 존재하지 않는다. 

 

 

이제 예시 코드를 살펴보자.

 

 

#include <iostream>
using namespace std;

class Circle {
public: 
	int radius;
	Circle(); //매개변수가 없는 생성자
	Circle(int r); //매개변수가 있는 생성자
	double circleSize(); //원의 넓이를 구하는 함수
};

Circle::Circle() {
	radius = 10; //매개변수의 값 초기화
	cout << "반지름의 길이가 " << radius << "인 원이 생성되었습니다."<<endl;
}

Circle::Circle(int r) {
	radius = r; //매개변수에 값 할당
	cout << "반지름의 길이가 " << radius << " 인 원이 생성되었습니다."<<endl;
}

double Circle::circleSize() {
	return radius * radius * 3.14;
}

int main() {
	Circle circleA; //매개변수가 없는 생성자 생성
	double size = circleA.circleSize();
	cout << "원 A의 면적:" << size<<endl;

	Circle circleB(100);
	size = circleB.circleSize();
	cout << "원 B의 면적:" << size<<endl;
}

 

 

<실행 결과>

 

 

 

생성자를 통해 매개변수 radius의 값을 할당했다. 또, Circle 클래스 내에 생성자가 중복으로 존재하지만 매개변수의 유무에 따라 단 하나의 생성자만 실행되었다. 

 

 

이제 다음 코드를 살펴보자. 위 코드에는 치명적이진 않지만 한 가지 신경쓰이는 것이 있다. 바로 코드가 중복되는 부분이 있다는 것이다. cout<<"반지름의 길이가"<<radius<<"인 원이 생성되었습니다."<<endl; 라는 코드가 두 번이나 사용되었다. 코드의 길이는 간략화 할 수 있다면 간략화 하는 것이 좋다. 이 부분을 조금 더 간소화 해보도록 하자. 

 

 

#include <iostream>
using namespace std;

class Circle {
public:
	int radius;
	Circle(); //위임 생성자
	Circle(int r); //타겟 생성자
	double circleSize(); //원의 넓이를 구하는 함수
};

Circle::Circle():Circle(1) {}//위임 생성자
Circle::Circle(int r) {
	radius = r;
	cout << "반지름이 " << radius << "인 원이 생성되었습니다." << endl;
}

double Circle::circleSize() {
	return radius * radius * 3.14;
}

int main() {
	Circle circleA; //매개변수가 없는 생성자 생성
	double size = circleA.circleSize();
	cout << "원 A의 면적:" << size << endl;

	Circle circleB(100);
	size = circleB.circleSize();
	cout << "원 B의 면적:" << size << endl;
}

 

<실행 결과>

 

 

위임 생성자는 생성자를 실행하는 생성자를 의미한다. 위 예시코드를 살펴보면 매개변수가 없는 생성자 Circle()은 매개변수가 있는 생성자인 Circle(int r)을 생성하는 역할을 하고있다. 두 코드의 내용은 동일하니 두 번씩 작성해줄 필요 없이 Circle()은 Circle(int r)을 불러내어 Circle(int r)이 가진 코드를 그대로 사용하면 되는 것이다. 

 

<위임 생성자>

Circle::Circle():Circle(1){};
클래스::위임생성자:타겟생성자;

 

 


 

 

 

우리가 생성자를 선언하지 않아도 컴파일러는 자동으로 생성자를 만들어낸다. 

 

 

#include <iostream>
using namespace std;

//원의 넓이를 구하는 클래스

class Circle {
public:
	int radius;
	double circleSize();
};

double Circle::circleSize() {
	return radius * radius * 3.14;
}

int main() {
	Circle circle;

	cout << "원의 반지름을 구하는 프로그램입니다." << endl;
	cout << "숫자 외 문자가 입력될 시 모든 값은 0으로 반환됩니다." << endl;
	cout << endl;

	cout << "원의 반지름을 입력하세요:";
	cin >> circle.radius;

	cout << "원의 넓이:" << circle.circleSize();
	cout << endl;
	
}

 

 

지난 게시글에서 작성한 이 코드가 무사히 작동되는 것 또한 컴파일러가 자동으로 생성자를 만들어냈기 때문이다. 이 코드를 잘 살펴보면 생성자를 선언한 부분이 아무 곳에도 보이지 않는다. 우리는 이 때 만들어지는 생성자를 기본 생성자 또는 디폴트 생성자라고 부른다. 하지만 만약, 하나라도 생성자를 선언했다면 기본 생성자는 만들어지지 않으니 이 점에 반드시 유의해야 한다. 

 

 

class circle{
public:
        int radius;
        Circle(int r); //생성자
        double circleSize();
}

Circle::circle(int r){
        radius=r;
)

int main{
        Circle circleA(10); //생성가능
        Circle circleB; //생성불가능. 클래스에서 이미 생성자를 선언했기 때문에 매개변수가 없는 기본 생성자는 만들어지지 않는다.

 

 

 

2. 소멸자(Destructor)

 

 

소멸자는 객체가 소멸될때 자동으로 호출되는 함수이다. 소멸자는 우리가 임의로 호출할 수 없으며 딱 하나만 존재한다. 때문에 소멸자는 다형성이 없다고 할 수 있다.

 

 

class Circle{
public:
        Circle();
        Circle(int r);
        ~Cicele(); //소멸자
}

Circle::~Circle(){
}

* 소멸자는 클래스의 이름앞에 ~를 붙인다.

 

 

소멸자는 객체가 사라질 때 할당받은 메모리를 해제하고 파일을 닫는 등, 마무리 작업을 하는 역할을 맡는다. 소멸자는 리턴타입의 선언이 불가능하며 값을 리턴하지도 않는다. 또, 소멸자는 생성자와 마찬가지로 선언되지 않으면 기본 소멸자가 자동으로 생성된다.

 

 

이제 예시코드를 한 번 살펴보자.

 

 

#include <iostream>
using namespace std;

class Circle {
public:
	int radius;
	Circle(); //매개변수가 없는 생성자
	Circle(int r); //매개변수가 있는 생성자
	~Circle(); //소멸자
};

Circle::Circle() {
	radius = 10; //매개변수의 값 초기화
	cout << "반지름의 길이가 " << radius << "인 원이 생성되었습니다." << endl;
}

Circle::Circle(int r) {
	radius = r; //매개변수에 값 할당
	cout << "반지름의 길이가 " << radius << " 인 원이 생성되었습니다." << endl;
}

Circle::~Circle() {
	cout << "반지름의 길이가 " << radius << " 인 원이 소멸되었습니다." << endl;
}

int main() {
	Circle circleA;
	Circle circleB(100);
	return 0;
}

 

<실행 결과>

 

 

메인 함수에서 따로 소멸자를 호출하지 않았지만 생성자와 함께 소멸자가 호출됨을 확인할 수 있다. 

 

 

 

 

3. 생성자와 소멸자의 실행순서

 

생성자와 소멸자의 실행순서는 객체가 선언된 위치에 따라 분류할 수 있다. 함수 내에 선언된 지역객체는 해당 함수가 종료되면 소멸되고 함수의 바깥에 선언된 전역객체는 프로그램이 종료될때 함께 소멸된다. 

 

객체의 생성 순서에도 차이가 있다. 전역 객체는 프로그램에 선언된 순서대로, 지역 객체는 함수가 호출되는 순간에 순서대로 생성된다. 객체가 소멸할때에는 역순으로 소멸하게 된다. (전역객체는 프로그램이 종료될때, 지역객체는 함수가 종료될때.)

 

만약 new를 이용해 생성된 동적객체라면 new를 실행하는 순간 객체가 생성되고 delete 연산자를 실행할때 객체가 소멸하게 된다.

 

 

이제 예시코드를 한 번 살펴보자.

 

 

#include <iostream>
using namespace std;

class Circle {
public:
	int radius;
	Circle(); //생성자1
	Circle(int r); //생성자2
	~Circle(); //소멸자
};

Circle::Circle() {
	radius = 10;
	cout << "반지름이 " << radius << " 인 원이 생성되었습니다." << endl;
}

Circle::Circle(int r) {
	radius = r;
	cout << "반지름이 " << radius << " 인 원이 생성되었습니다." << endl;
}

Circle::~Circle() {
	cout << "반지름이 " << radius << " 인 원이 소멸되었습니다." << endl;
}

Circle globalCircleA(100); //전역객체로 생성된 반지름이 100인 원
Circle globalCircleB(200); //전역객체로 생성된 반지름이 200인 원

void func() {
	Circle fCircleC(1000); //지역객체로 생성된 반지름이 1000인 원
	Circle fCircleD(2000); //지역객체로 생성된 반지름이 2000인 원
}

int main() {
	Circle circleE; //지역객체로 생성된 반지름이 10인 원
	Circle circleF(20);//지역객체로 생성된 반지름이 20인 원
	func(); //func함수 실행
}

 

 

<실행 결과>

 

 

가장 먼저 전역객체로 선언된 객체가 선언된 순서대로 실행되었다. 그 다음으로 지역객체로 선언된 객체가 호출된 순서대로 실행되었으며 소멸은 실행된 역순으로 진행되었다. 

 

 


 

(예제코드 소스)

 

https://github.com/wjdals819/Cpp_Project.git

 

GitHub - wjdals819/Cpp_Project: C++ 실습코드

C++ 실습코드. Contribute to wjdals819/Cpp_Project development by creating an account on GitHub.

github.com