프로그래밍 언어/C++

5) 생성자와 소멸자

busy맨 2023. 11. 1. 23:19

1. 생성자와 소멸자

1) 생성자와 소멸자

  • 생성자와 소멸자는 자동으로 호출되는 함수
  • 리턴 타입 없음
  • 생성자
    • 객체가 생성되는 시점에서 자동으로 호출되는 멤버 함수
    • 객체가 생성될 때 객체가 필요한 초기화를 위해
    • "클래스이름()" 형식
    • 디폴트 매개변수 사용 가능
  • 소멸자
    • 객체가 소멸되는 시점에서 자동으로 호출되는 멤버함수
    • 객체가 소멸될 때 객체의 정리를 위해
    • "~클래스이름()"형식
    • 인자를 가질 수 없기 때문에 오버로딩 불가

 

2) 생성자 특징

  • 객체 생성 시 오직 한 번만 호출
    • 각 객체마다 생성자 실행
  • 생성자는 중복 가능
    • 생성자는 한 클래스 내에 여러 개 가능
    • 중복된 생성자 중 하나 만 실행
class Circle	
{
    int radius;
    Circle();	//클래스 이름과 동일
    Circle(int r);	//클래스에 대한 생성자를 2개 생성
    ..........................................
};

Circle::Circle()	//생성자 함수 구현
{
	radius = 1;
}

Circle::Circle(int r)	//매개 변수를 가진 생성자 함수 구현
{
	radius = r;
}
  • Ex) 2개의 생성자를 가진 Circle 클래스
#include <iostream> 
using namespace std; 

class Circle {
public:
    int radius;
    Circle(); // 매개 변수 없는 생성자
    Circle(int r); // 매개 변수 있는 생성자
    double getArea();
}; 

Circle::Circle() 
{
    radius = 1;
    cout << "반지름 " << radius << " 원 생성" << endl;
}

Circle::Circle(int r) 
{
    radius = r;
    cout << "반지름 " << radius << " 원 생성" << endl;
}
double Circle::getArea() 
{
	return 3.14*radius*radius;
}
int main() 
{
    Circle donut; // 매개 변수 없는 생성자 호출
    double area = donut.getArea();
    cout << "donut 면적은 " << area << endl;
    
    Circle pizza(30); // 매개 변수 있는 생성자 호출
    area = pizza.getArea();
    cout << "pizza 면적은 " << area << endl;
}

result

3) 디폴트 생성자

  • 매개 변수가 없는 생성자(기본 생성자)
  • 객체를 생성할 때 별도로 생성자를 지정하지 않으면 항상 디폴트 생성자로 초기화 됨
    • 컴파일러에 의해 디폴트 생성자가 자동 생성
  • 생성자가 하나라도 있는 경우 디폴트 생성자 생성 X
class Circle {
    .....
    Circle(); // 기본 생성자
};

4) 소멸자 특징

  • 리턴 타입 선언 불가
  • 중복 불가능
    • 소멸자는 한 클래스 내에 오직 한 개만
  • 매개 변수가 없는 함수
  • 소멸자가 선언되어 있지 않으면 디폴트 소멸자가 자동 생성
    • 컴파일러에 의해 디폴트 소멸자 코드 생성

 

  • Ex) Circle 클래스에 소멸자 작성 및 실행
#include <iostream> 
using namespace std;

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

Circle::Circle() {
    radius = 1;
    cout << "반지름 " << radius << " 원 생성" << endl;
}

Circle::Circle(int r) {
    radius = r;
    cout << "반지름 " << radius << " 원 생성" << endl;
}

Circle::~Circle() {
	cout << "반지름 " << radius << " 원 소멸" << endl;
}

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

int main() 
{
    Circle donut; 
    Circle pizza(30); 
    return 0;
}

result

5) 생성자 / 소멸자 실행 순서

  • 객체가 선언된 위치에 따른 분류
    • 지역 객체
      • 함수 내에 선언된 객체로서, 함수가 종료되면 소멸
    • 전역 객체
      • 함수의 바깥에 선언된 객체로서, 프로그램이 종료할 때 소멸
  • 객체 생성 순서
    • 전역 객체는 프로그램에 선언된 순서로 생성
    • 지역 객체는 함수가 호출되는 순간에 순서대로 생성
  • 객체 소멸 순서
    • 함수가 종료되면, 지역 객체가 생성된 순서의 역순으로 소멸
    • 프로그램이 종료되면, 전역 객체가 생성된 순서의 역순으로 소멸
#include <iostream> 
using namespace std; 

class Circle {
public:
    int radius; 
    Circle(); 
    Circle(int r); 
    ~Circle(); 
    double getArea(); 
}; 

Circle::Circle() {
    radius = 1;
    cout << "반지름 " << radius << " 원 생성" << endl;
}

Circle::Circle(int r) {
    radius = r;
    cout << "반지름 " << radius << " 원 생성" << endl;
}

Circle::~Circle() {
	cout << "반지름 " << radius << " 원 소멸" << endl;
}

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

Circle globalDonut(1000);	//전역 객체
Circle globalPizza(2000);

void f() {
    Circle fDonut(100);	//함수 안의 지역 객체
    Circle fPizza(200);
}
int main() 
{
    Circle mainDonut;	//main() 문 안의 지역 객체
    Circle mainPizza(30);
    f();
}

result
생성과 소멸 과정

6) 객체 간의 초기화와 대입

  • 같은 클래스의 객체 간에 서로 초기화나 대입이 가능
  • 초기화
    • 복사 생성자 이용
      • 같은 클래스의 다른 객체와 같은 값을 같도록 초기화
      • 클래스의 멤버 변수를 1대1로 초기화
  • 대입
    • 대입 연산자 이용
      • 같은 클래스의 다른 객체 값을 대입
      • 클래스의 멤버 변수를 1대1로 대임
Stack s1(5);

Stack s2 = s1; // 객체 간의 초기화 = 복사생성자

Stack s3;
s3 = s1; // 객체 간의 대입

 

2. 분할 컴파일

1) 바람직한 프로그램 작성법

  • 클래스 선언부
    • 헤더파일(*.h)에 저장
  • 클래스 구현부
    • cpp 파일(*.cpp)에 저장
    • 클래스가 선언된 헤더파일을 include해야함
  • main() 등 전역 함수나 변수는 다름 cpp파일에 분산 저장
    • 필요 시 클래스가 선언된 헤더파일 include

 

  • Ex)
  • Circle.h(클래스 선언부)
class Circle {
private:
    int radius;
    public:
    void SetRadis();
    int GetRadius();
    double getArea();
};
  • Circle.cpp(클래스 구현부)
#include <iostream> 
using namespace std; 

#include "Circle.h"

void Circle::setRadis(int r) {
	radius = r;
}

int getRadius() {
	return radius;
}

double Circle::getArea() {
	return 3.14*radius*radius;
}
  • main.cpp
#include <iostream> 
using namespace std; 
#include "Circle.h"

int main() {
    Circle donut; 
    donut.setRadius(1);
    double area = donut.getArea(); 
    cout << "donut 면적은 ";
    cout << area << endl;
    
    Circle pizza;
    pizza.setRadius(30); 
    area = pizza.getArea(); 
    cout << "pizza 면적은 ";
    cout << area << endl; 
}

 

2) 헤더 파일의 중복 include 문제

#include <iostream> 
using namespace std;

#include "Circle.h"
#include "Circle.h" // 컴파일 오류 발생 : 'class'형식 재정의
#include "Circle.h"

int main() {
	...........
}

 

  • 조건 컴파일로 해결
    • 규칙
      1. 헤더 파일의 이름을 따서 심볼을 생성(Ex. TEST_H )
      2. 헤더 파일의 제일 앞에 #ifndef, #define 명령을 추가
      3. 헤더 파일의 제일 끝에 #endif 명령을 추가
#ifndef CIRCLE_H
#define CIRCLE_H

class Circle {
private:
	int radius;
public:
    void setRadius(int r);
    int getRadius()
    double getArea();
}; 

#endif

'프로그래밍 언어 > C++' 카테고리의 다른 글

7) 여러가지 객체의 생성 방법  (1) 2023.11.03
6) 접근 지정자, static 멤버  (0) 2023.11.02
4) 클래스와 객체의 기본  (1) 2023.11.01
3) 개선된 함수 기능  (0) 2023.11.01
2) 포인터와 레퍼런스  (1) 2023.11.01