C++ 생성자(constructor)와 파괴자(destructor)

프로그래밍/C++ 2019.10.14 댓글 Plorence

먼저 생성자와 파괴자는 클래스를 설계할 때 사용합니다. 생성자, 파괴자는 둘 다 표준 함수입니다.
일반적으로 모든 객체는 그것을 생성할때 초기화하는 것이 가장 바람직합니다.

생성자

생성자(멤버 함수)의 이름은 클래스의 이름과 동일합니다.(이건 규칙입니다.)
예를들어서 클래스의 이름이 Person이라면 생성자의 이름도 Person이어야 합니다.
생성자가 멤버 함수와의 큰 차이점이 있다면, 바로 반환형이 없습니다.
class Person {
    int Age;
    Person(int age) { //Person 클래스의 생성자
        Age = age;
    }
};
생성자도 디폴트 매개변수 사용이 가능하며 디폴트 매개변수를 사용할때 지켜야 할 규칙은 마찬가지로 지켜야 합니다.
 

표준함수인데 반드시 사용을 안 해도 되는 이유

어떤 객체를 설계하느냐에 따라 사용여부가 결정되겠지만,대부분 생성자가 사용되긴 합니다.
#include <iostream>
class Person
{
private:
    int age;
};
int main(void) {
    Person per;
}
위 코드를 보면 클래스에서는 생성자가 정의되어 있지 않습니다.
이처럼 클래스를 설계할때 생성자를 정의하지 않았다면 컴파일러가 디폴트 생성자를 제공합니다.
 

디폴트 생성자란?

디폴트 생성자는 다른 생성자와는 다르게 파라미터가 없습니다.
물론 파라미터를 안써도 내부 멤버 변수 초기화를 위해 사용하는 경우도 있습니다.
#include <iostream>
class Person
{
private:
    int age;
public:
    Person(){
    }
};
위 예제코드와 동일합니다. 다만 정의를 안하면 컴파일러가 알아서 해주는거 뿐입니다.
 

생성자를 통하여 객체의 멤버를 초기화 하는방법

#include <iostream>
class Person
{
private:
    int age;
public:
    Person(int p_age) { 
        age = p_age;
    }
};
int main(void) {
    Person per1(12); //암시적 호출
    Person per2 = Person(12); //명시적 호출
    Person per3{ 12 }; //C++11부터
    Person per4 = { 12 }; //C++11부터
    Person * per5 = new Person(12); //동적할당
}
초기화 할 수 있는 방법이 다양한데 원하시는거 골라쓰시면 될것 같습니다.
 
이때 주의할 점은, 디폴트가 아닌 생성자의 암시적인 형식을 지정하면 안됩니다.
class Person
{
private:
    int age;
public:
};
이러한 클래스가 있을 때
Person per();
의 선언은 함수를 선언한다는 의미입니다.
즉 디폴트 생성자를 암시적(괄호를 쓰는 방법)으로 호출할 때에는 괄호를 사용하면 안됩니다.
#include <iostream>
class Person
{
private:
       int age;
public:
       Person() {
              std::cout << "생성자";
       }
       Person(int p_age) {
              age = p_age;
       }
};
int main(void) {
       Person per1;
       Person per2();
       
}
위 코드의 출력 결과는 아래와 같습니다.
생성자
 

파괴자

파괴자는 생성자의 반대입니다.
객체의 수명이 끝나는 시점에서 파괴자를 자동으로 호출합니다.
파괴자가 생성자랑 다른점은 매개변수를 가질 수 없습니다. 다른점은 똑같습니다.
이름짓는 법도 비슷합니다. 생성자 이름 앞에다가 ~를 붙여주면 파괴자가 됩니다.
class Person
{
private:
    int age;
public:
    ~Person() { //파괴자
    }
};
일반적으로 사용자가 코드에 명시적으로 파괴자를 호출하면 안 됩니다.
파괴자가 호출 시점은 아래 세 가지 경우입니다.
  • 스코프를 벗어났을때
  • new를 사용하여 객체를 생성하고 delete 하였을때
  • 임시 객체를 생성했을 경우에 프로그램은 객체의 사용을 마쳤을때
 

파괴자가 필요한 이유

만약 클래스 내부에서 동적할당이 일어난다면 메모리 해제도 필요합니다.
C++에서 메모리 관리는 직접 해야하기 때문입니다.
파괴자가 호출됐다는 건, 더이상 그 객체가 쓰이지 않는다는겁니다.
즉 동적할당 했던 건 더이상 쓰이지 않으니 메모리 해제를 해야 메모리 누수(memory leak)이 발생하지 않습니다.
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string.h>
class Person {
       char * name;
public:
       Person(const char * strName) {
              name = new char[strlen(strName) + 1];
              strncpy(name, strName, strlen(strName)+1);
       }
       ~Person() { //파괴자
              delete[] name; //동적할당 했던 것을 해제한다.
       }
       void Print() {
              std::cout << name;
       }
};
int main(void) {
       Person per("asd");
       per.Print();
}
 
 
 

댓글