카테고리 없음

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

거북이 코딩 2024. 2. 11. 18:28

생성자

 

 생성자란 반환값이 없고 클래스와 이름이 같은 멤버함수로 클래스 객체가 생성될 때 한 번만 호출되는 함수입니다. 생성자도 함수이므로 오버로딩이 가능하고 디폴트 값을 설정할 수 있습니다. 그럼 간단한 생성자 예제를 작성해 보겠습니다.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>

using namespace std;

class A {
private:
    int num1;
public:
    A(int num) {    //생성자 정의
        num1 = num;
    }
    void ShowNum() {
        cout << num1 << endl;
    }
};

int main(void) {
    A clsA(30);    //30을 생성자에 인자로 전달하며 클래스 생성
    A* clsA2 = new A(50);    //50을 생성자에 인자로 전달하며 동적할당으로 클래스 생성
    clsA.ShowNum();
    clsA2->ShowNum();
    delete clsA2;
    return 0;
}
30
50

디폴트 생성자

 

 모든 객체는 만들어질 때 하나의 생성자가 호출되어야 합니다. 그래서 우리가 명시적으로 생성자를 정의하지 않는다면 컴파일러에 의해 디폴트 생성자가 자동으로 삽입됩니다. 디폴트 생성자는 "클래스이름 (){}"의 형태로 인자와 내용이 없는 형태를 띠고 있습니다. 하지만 우리가 위 코드처럼 생성자를 정의해 주게 되면 디폴트 생성자는 삽입되지 않습니다. 그래서 평소처럼 인자를 전달하지 않고 클래스를 생성하게 되면 오류가 발생합니다. 이를 막기 위해 생성자 오버로딩을 사용합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>

using namespace std;

class A {
private:
    int num1;
public:
    A(int num) {
        num1 = num;
    }
    A() {}    //생성자 오버로딩
    void ShowNum() {
        cout << num1 << endl;
    }
};

int main(void) {
    A clsA(30);
    A* clsA2 = new A(50);
    A clsA3;    //생성자 오버로딩을 해서 오류가 발생하지 않음
    clsA.ShowNum();
    clsA2->ShowNum();
    delete clsA2;
    return 0;
}
30
50

 

이니셜라이저

 

 이니셜라이저란 클래스의 생성자가 호출될 때 멤버변수나 멤버객체들을 초기화 해주는 장치입니다. 생성자이름(인자) : 이니셜라이저 {생성자내용}의 형태로 사용합니다. 인자를 받으면 num1에 저장하고 인자를 받지 않으면 num1에 0을 저장하는 간단한 이니셜라이저의 예를 들어보겠습니다.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>

using namespace std;

class A {
private:
    int num1;
public:
    A(int num) :num1(num) {
    }
    A() : num1(0) {}
    void ShowNum() {
        cout << num1 << endl;
    }
};

int main(void) {
    A clsA(30);
    A* clsA2 = new A(50);
    A clsA3;
    clsA.ShowNum();
    clsA2->ShowNum();
    clsA3.ShowNum();
    delete clsA2;
    return 0;
}
30
50
0

 

 초기화대상(초기화값) 의 형태로 이니셜라이저를 작성합니다. 이니셜라이저로 초기화하는 방식은 생성자 내부에 써서 초기화하는 방식에 비하여 두 가지 장점이 있습니다. 초기화의 대상을 명확히 인식할 수 있다는 점과 성능이 약간 향상된다는 점입니다. 이니셜라이저를 사용하면 변수의 선언과 동시에 초기화가 되어 "int num1 = num;"의 형태가 됩니다. 하지만 내부에 쓰게 되면 "int num1; num1=num;"의 형태로 두줄에 걸쳐 쓰는 것이 되기 때문에 이니셜라이저를 사용하면 약간의 성능향상이 이루어지는 것입니다.

 

소멸자

 

 소멸자란 생성자와 반대되는 개념의 함수로 객체가 소멸될때 한번 호출되는 함수입니다. ~클래스이름(){소멸자내용}의 형태로 작성합니다. 주로 생성자 내에서 new를 이용한 동적할당을 했을 때 delete로 해제하기 위해서 사용합니다. 동적할당을 해제하는 간단한 소멸자의 예를 들어보겠습니다.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>

using namespace std;

class Person {
private:
    char* name;
    int age;
public:
    Person(const char* NAME, int AGE) : age(AGE) {    //생성자
        name = new char[strlen(NAME) + 1];
        strcpy(name, NAME);
        cout << "생성자 호출" << endl;
    }
    void ShowPersonInfo() {
        cout << "이름: " << name << endl;
        cout << "나이: " << age << endl;
    }
    ~Person() {    //소멸자
        delete[]name;
        cout << "소멸자 호출" << endl;
    }
};

int main(void) {
    Person man("신용준", 23);
    man.ShowPersonInfo();
    return 0;
}
생성자 호출
이름: 신용준
나이: 23
소멸자 호출

 

 소멸자를 사용하면 프로그래머의 실수로 메모리 누수가 발생하는것을 방지할 수 있습니다. 

 

마치며

 

 오늘은 생성자와 소멸자 그리고 이니셜라이저를 공부해 보았습니다. 제가 든 예시들은 간단한 예시들이지만 활용만 잘한다면 클래스를 만들어 나가는 데에 매우 유용한 도구가 될 것입니다.