Language/C++

📌 Chapter 04 - 클래스 고급 개념 - 02

e-cko 2025. 8. 9. 14:43
반응형
반응형

4. 생성자 (Constructor)

1) 생성자의 개념

  • 생성자(Constructor)는 객체가 생성될 때 자동으로 호출되는 특수한 멤버 함수이다.
  • 객체는 반드시 하나의 생성자를 거쳐야생성된 객체로 간주한다.
  • 생성자의 이름은 클래스명과 동일하게 선언한다.
  • 반환형이 없고 값을 반환하지 않으며, 기본적으로 public 접근 제어를 갖는다.
  • 생성자는 객체 생성 시 단 한 번만 호출된다.
  • 오버로딩이 가능하며, 매개변수에 기본값 설정도 할 수 있다.
  • 생성자를 정의하지 않을 경우, 컴파일러가 기본 생성자(Default Constructor)를 자동 삽입한다.

⛏️ 생성자와 Init 함수의 차이
· Init()
은 명시적으로 호출해야 하는 일반 함수이다.
·
반면 생성자는 객체 생성과 동시에 자동 호출된다.

예시

class Ex {

public:

    int a, b;

 

    // 생성자 정의

    Ex() {

        a = 0;

        b = 0;

    }

 

    Ex(int x, int y) {

        a = x;

        b = y;

    }

};

 

int main() {

    Ex e1;        // 기본 생성자 호출

    Ex e2(10, 20); // 오버로딩된 생성자 호출

}

⚠️ 함수 원형 선언과 생성자 호출의 혼동

Ex ex(); // 이것은 객체 생성이 아닌 함수 원형 선언이다.

· 위와 같은 문법은 C++에서 인자를 받지 않는 함수의 원형으로 인식된다.
·
따라서 객체 생성을 위해서는 반드시 괄호 없이 Ex ex; 형태로 작성해야 한다.


2) 멤버 이니셜라이저(Member Initializer)

  • 생성자에서 멤버 변수 또는 멤버 객체의 초기화를 위해 사용하는 구문이다.
  • : 뒤에 나오는 구문을 통해 초기화를 수행한다.
  • ⛏️ 성능적으로도 이점이 있으며, 특히 const 변수참조자는 반드시 이 방식으로 초기화해야 한다.

예시

class Ex {

    const int id;

    int& ref;

 

public:

    Ex(int x, int& r) : id(x), ref(r) {} // 멤버 이니셜라이저 사용

};

비교

// 멤버 이니셜라이저 방식

Ex(int n) : a(n) {}

 

// 생성자 본문에서 초기화

Ex(int n) {

    a = n;

}

· 두 방식은 동작상 유사하나, 멤버 이니셜라이저는 컴파일 단계에서 바이너리 코드 최적화가 가능하다.


3) 기본 생성자(Default Constructor)

  • 매개변수가 없는 생성자를 의미한다.
  • 사용자가 생성자를 하나도 정의하지 않으면, 컴파일러가 자동으로 삽입한다.
  • 생성자가 하나라도 정의되어 있으면, 기본 생성자가 필요할 경우 직접 명시해야 한다.

주의
· malloc()
을 이용해 객체를 생성하면 생성자가 호출되지 않는다.
· new
연산자를 사용해야 생성자가 자동으로 호출된다.


4) private 생성자

  • 클래스 내부 또는 특정 함수에서만 호출 가능한 생성자이다.
  • 객체의 생성을 제한하거나, 싱글톤 패턴 등을 구현할 때 유용하다.

예시

class OnlyFactory {

private:

    OnlyFactory() {} // 외부에서는 생성 불가

 

public:

    static OnlyFactory create() {

        return OnlyFactory(); // 내부에서만 생성 가능

    }

};


5. 소멸자(Destructor)

  • 객체가 소멸될 때 자동으로 호출되는 함수이다.
  • 클래스명 앞에 ~ 기호를 붙여서 선언한다.
  • 반환형과 매개변수가 없으며, 오버로딩이 불가능하다.
  • 객체가 소멸될 때 리소스 해제 등의 정리 작업에 사용된다.

예시

class Ex {

public:

    ~Ex() {

        std::cout << "객체가 소멸되었습니다." << std::endl;

    }

};


6. 객체 배열

1) 일반 배열

  • 클래스 객체를 배열 형태로 선언할 수 있다.
  • Ex ex[10]; 형태로 선언하면, 내부적으로 기본 생성자가 10번 호출된다.
  • 생성자에 인자를 넘기려면 포인터 배열을 사용해야 한다.

주의
·
생성자에 매개변수가 있는 경우, 해당 생성자를 사용할 수 없기 때문에 기본 생성자가 반드시 필요하다.


2) 포인터 배열

  • 객체를 포인터로 관리할 경우, 배열 요소마다 서로 다른 생성자를 호출할 수 있다.

예시

Ex* ex[3];

ex[0] = new Ex(1, 2);

ex[1] = new Ex(3, 4);

ex[2] = new Ex(5, 6);


7. this 포인터

  • this자기 자신 객체의 주소를 담고 있는 포인터이다.
  • 주로 매개변수명과 멤버변수가 겹칠 때 사용한다.
  • 멤버 이니셜라이저에서는 사용할 수 없다.

예시

class Ex {

    int x;

 

public:

    Ex(int x) {

        this->x = x; // 매개변수와 멤버변수 구분

    }

 

    Ex* getThis() {

        return this;

    }

};


1) 참조 반환(Self-Reference)

  • 자기 자신 객체를 참조자로 반환하고자 할 때 사용한다.

예시

class Ex {

public:

    Ex& getSelf() {

        return *this; // 자기 참조 반환

    }

};


마무리 정리
1️
생성자는 클래스명과 동일하며 반환형이 없다.
2️
생성자 오버로딩과 기본값 설정이 가능하다.
3️
멤버 이니셜라이저는 const 및 참조자 초기화에 필수이다.
4️
소멸자는 ~기호를 사용하며, 반환형·매개변수 없이 정의한다.
5️
객체 배열은 기본 생성자를 필요로 하며, 포인터 배열로 유연하게 관리할 수 있다.
6️
this 포인터는 멤버 함수 내에서 자기 자신 객체의 주소를 의미한다.


💻 전체 예제 코드: 생성자, 소멸자, this 포인터 통합 예제

#include <iostream>

#include <string>

using namespace std;

 

// 📌 클래스 정의

class Ex {

private:

    const int id;      // const 멤버 변수 (멤버 이니셜라이저로만 초기화 가능)

    int x, y;          // 일반 멤버 변수

    string name;       // 문자열 멤버 변수

 

public:

    // 1. 기본 생성자

    Ex() : id(0), x(0), y(0), name("default") {

        cout << "[기본 생성자] 객체 생성됨: id=" << id << ", name=" << name << endl;

    }

 

    // 2. 오버로딩된 생성자 (멤버 이니셜라이저 사용)

    Ex(int _id, int _x, int _y, string _name)

        : id(_id), x(_x), y(_y), name(_name) {

        cout << "[오버로딩 생성자] 객체 생성됨: id=" << id << ", name=" << name << endl;

    }

 

    // 3. this 포인터 사용 - 멤버 변수 설정

    void setValues(int x, int y) {

        this->x = x;  // 매개변수 x와 멤버변수 x 구분

        this->y = y;

    }

 

    // 4. this 포인터를 통해 자기 자신 반환 (Self-reference)

    Ex& getSelf() {

        return *this;

    }

 

    // 5. 정보 출력 함수

    void printInfo() const {

        cout << "객체 정보: id=" << id << ", name=" << name

             << ", x=" << x << ", y=" << y << endl;

    }

 

    // 6. 소멸자

    ~Ex() {

        cout << "[소멸자] 객체 소멸됨: id=" << id << ", name=" << name << endl;

    }

};

 

int main() {

    // 1. 기본 생성자 호출

    Ex obj1;

 

    // 2. 오버로딩 생성자 호출

    Ex obj2(1, 10, 20, "hello");

 

    // 3. this 포인터로 멤버값 설정

    obj1.setValues(100, 200);

    obj1.printInfo();

 

    // 4. Self-reference 활용

    Ex& ref = obj2.getSelf();

    ref.printInfo();

 

    // 5. 객체 배열 생성 (기본 생성자 필요)

    Ex arr[2]; // 기본 생성자 2번 호출됨

 

    // 6. 포인터 배열을 통한 객체 생성

    Ex* pArr[2];

    pArr[0] = new Ex(2, 11, 22, "A");

    pArr[1] = new Ex(3, 33, 44, "B");

 

    // 7. 동적으로 생성된 객체 정보 출력

    pArr[0]->printInfo();

    pArr[1]->printInfo();

 

    // 8. 메모리 해제

    delete pArr[0];

    delete pArr[1];

 

    return 0;

}


📦 예상 출력 결과

[기본 생성자] 객체 생성됨: id=0, name=default

[오버로딩 생성자] 객체 생성됨: id=1, name=hello

객체 정보: id=0, name=default, x=100, y=200

객체 정보: id=1, name=hello, x=10, y=20

[기본 생성자] 객체 생성됨: id=0, name=default

[기본 생성자] 객체 생성됨: id=0, name=default

[오버로딩 생성자] 객체 생성됨: id=2, name=A

[오버로딩 생성자] 객체 생성됨: id=3, name=B

객체 정보: id=2, name=A, x=11, y=22

객체 정보: id=3, name=B, x=33, y=44

[소멸자] 객체 소멸됨: id=2, name=A

[소멸자] 객체 소멸됨: id=3, name=B

[소멸자] 객체 소멸됨: id=0, name=default

[소멸자] 객체 소멸됨: id=0, name=default

[소멸자] 객체 소멸됨: id=1, name=hello

[소멸자] 객체 소멸됨: id=0, name=default

 

반응형