[C++] 생성자

cheon2308
|2023. 11. 27. 20:00
728x90

황기태 저자의 명품 C++ Programming 개정판을 읽고 학습한 내용을 정리한 포스트입니다!

https://search.shopping.naver.com/book/catalog/32436115747

 

C++ Programming : 네이버 도서

네이버 도서 상세정보를 제공합니다.

search.shopping.naver.com

 # 생성자

  • C++에서 객체를 생성할 때는 객체를 초기화할 수 있다.
  • 클래스에서는 객체가 생성될 때 자동으로 실행되는 생성자(constructor)라는 특별한 멤버 함수를 이용하여 객체를 초기화한다.
특징
  1. 생성자의 목적은 객체가 생성될 때 필요한 초기 작업을 위함이다.
    • ex) 멤버 변수의 값을 특정 값으로 설정(변수 초기화)하거나, 메모리를 동적으로 할당 받기 등 객체를 사용하기 전 필요한 조치를 하기 위함
  2. 생성자 함수는 오직 한 번만 실행
    • 각 객체가 생성되는 시점에 오직 한 번 자동 실행
  3. 이름은 클래스 이름과 동일하게 작성되어야 한다.
    • 반드시 클래스 이름과 동일하게 작성함으로써 다른 멤버 함수와 쉽게 구분
  4. 생성자 함수의 원형에 리턴 타입 선언하지 않는다.
    • void도 설정하면 안된다.
    • 종료를 위해서는 return; 만 사용해야됨
  5. 생성자는 중복 가능
    • 매개 변수 또는 타입이 달라야한다.

객체가 생성되는 과정

 

위임 생성자(delegating constructor) => 생성자가 다른 생성자 호출

  • 보통 한 글래스의 생성자들에는 대게 객체를 초기화하는 비슷한 코드가 중복
  • 따라서, 중복된 초기화 코드를 하나의 생성자 코드로 몰고, 다른 생성자에서 호출하도록 만든 것!
#include <iostream> 
using namespace std; 

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

Circle::Circle() : Circle(1) { }// 타겟 생성자

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;
}

 

  • Circle() 생성자가 호출되면 Circle() 생성자는 자신의 코드를 실행하기 전에 Circle(int r) 생성자를 호출하여, r에 1을 넘겨주어 radius를 1로 초기화하고 반지름과 원 생성을 대신 출력
  • 객체의 초기화 작업이 코딩된 Circle(int r) => 타겟 생성자
  • Circle() 생성자는 객체의 초기화를 다른 생성자에 위임한다고 해서 위임 생성자(delegating constructor)라고 부름
  • 타겟 생성자에 객체 초기화를 전담 => 객체의 생성 과정이 명료(clear)해지고 단순(simple) 해진다.

# 생성자와 멤버 변수 초기화

1. 생성자 몸체에서 멤버 변수 초기화

  • 따지고 보면 초기화가 아닌 할당이다.
  • 기본 변수가 생성된 후, 멤버 변수값이 할당 된다.
class Point {
	int x=0; y=0;// 복사 초기화
    double value2(2.2); // 직접 초기화
    char value3 {'c'}; 유니폼 초기화
	...
}

 

2. 생성자 서두에 초깃값으로 초기화 => 생성자 이니셜라이져( = 멤버 이니셜라이져 = 생성자 멤버 초기화 리스)

  • 생성자 이름 뒤에 콜론(:)을 붙여서 표현
class Point {
	int x, y;
public:
	// 1번째 방법
	Point() : x(1), y(1); // 멤버 초기화 리스트를 통해 멤버 변수를 직접 초기화
    {
    // 멤버 변수를 여기서 할당할 필요가 없다.
    }
	Point(int a, int b) : x(a), y(b);
	void show() { cout << "(" << x << ", " << y << ")" << endl; }
};

 

  • 멤버 이니셜라이져를 사용하면 초기화의 대상이 명확히 인식이 가능
멤버 이니셜라이저를 통해 멤버를 초기화해야만 하는 상황

 

1. 상수 멤버가 있을 때(const)

  • const 변수는 선언과 동시에 초기화를 해야만 오류가 나지 않음
class A {
private:
    const int num;
    
public:
     A() : num(0) {}
     
}

 

2. 레퍼런스 멤버가 있을 때 (레퍼런스 데이터 멤버)

  • const 변수와 마찬가지로 선언과 동시에 초기화를 해야 함
#include <iostream>

class Point
{
    int x;
    int y;
    const int cval;   // const 변수
    int& r;
public:
    Point( int a, int b, int c) : x(a), y(b), cval(c), r(a)
    {
    //    cval = c; // 대입 : 에러 발생한다.
    //    x = a;    
    //    y = b;
    }
};

int main()
{
    Point p(1, 2, 10);
}

 

3. 클래스 안에 디폴트 생성자가 없는 타입이 멤버 데이터로 있을 때 반드시 멤버 초기화 리스트를 사용

  • Point의 경우 Point() 생성자가 없기 때문에 Rect(a,b, c, d)가 불렸을 때 p1, p2 멤버 초기화가 바로 되도록 아래 방식의 코딩이 되어야 함
class Point
{
    int x;
    int y;
public:
    Point( int a, int b) : x(a), y(b) {}
};

class Rect
{
    Point p1;
    Point p2;
public:
    
    Rect(int a, int b, int c, int d) : p1(a,b), p2(c,d) // 무조건 이런 초기화가 좋다.
    {
        // if, switch와 같은 제어문에 의한 값 셋팅이 필요하다면 이곳에 초기화 해줘야함.
        //p1(a, b);  
    }
};

int main()
{
    Rect r (1,2,3,4);
            // p1 Point 생성자
            // p2 Point 생성자
            // Rect 생성자
}

 

주의사항 : 초기화 순서는 멤버 데이터가 놓인 순서대로 초기화되므로 순서에 주의해야 된다.

 

기본 생성자(default constructor)

  • 클래스에 선언된 어떤 생성자도 없을 때 컴파일러가 자동으로 생성하는 생성자 => 매개변수 없음
  • 다만, 생성자가 하나라도 선언된 클래스의 경우, 컴파일러는 기본 생성자를 자동 삽입 xxxx

Rectangle 클래스를 만들며 위의 내용을 복습해보자.

#include <iostream> 
using namespace std; 

class Rectangle { 
public:
	int width, height; 
	Rectangle();
	Rectangle(int w, int h);
	Rectangle(int length);
	bool isSquare();
};

Rectangle::Rectangle() { // Rectangle::Rectangle() : Rectangle(1) {}로 해도 됨
	width = height = 1;
}

Rectangle::Rectangle(int w, int h) {
	width = w; height = h;
}

Rectangle::Rectangle(int length) { // Rectangle::Rectangle(int length) : Rectangle(length) {}로 해도 됨
	width = height = length;
}

bool Rectangle::isSquare() { // 정사각형이면 true를 리턴하는 멤버 함수
	if(width == height) return true;
	else return false;
}

int main() {
	Rectangle rect1; 
	Rectangle rect2(3, 5);
	Rectangle rect3(3);

	if(rect1.isSquare()) cout << "rect1은 정사각형이다." << endl;
	if(rect2.isSquare()) cout << "rect2는 정사각형이다." << endl;
	if(rect3.isSquare()) cout << "rect3는 정사각형이다." << endl;
}
728x90