본문 바로가기

전공 과목/C++

위임 생성자

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

한 클래스의 생성자들에는 대개 객체를 초기화하는 비슷한 코드가 중복된다. 

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

C++11부터는 중복된 초기화 코드르 하나의 생성자로 몰고, 다른 생성자에서 이 생성자를 호출할 수 있게 한다. 이 기능을 이용하면 앞의 코드는 간소화된다.

Circle::Circle() : Circle(1) {} // Circle(int r)의 생성자 호출, 위임 생성자

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

Circle() 생성자가 호출되면 Cirle() 생성자는 자신의 코드를 싱행하기 전에 Circle(int r) 생성자를 호출하여, r에 1을 넘겨주어 radius를 1로 초기화하고 반지름과 원 생성을 대신 출력하게 한다.

객체의 초기화 작업이 코딩된 Circle(int r)를 타겟 생성자라고 부르고, Circle() 생성자는 객체의 초기화를 다른 생성자에 위임하낟고 해서 위임 생성자라고 부른다. 타겟 생성자에 객체 초기화를 전담시킴으로써 객체의 생성 과정이 명료해지고 단순해진다. 위임 생성자에서는 타겟 생성자를 호출한 뒤, 자신만의 코드를 추가하면 된다.

#include <iostream>

class Circle {
	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();
	std::cout << "donut의 면적은 " << area << "입니다." << std::endl;
 
 	Circle pizza(30);
	area = pizza.getArea();
	std::cout << "pizza의 면적은 " << area << "입니다." << std::endl;
}

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

클래스의 멤버 변수들은 자동으로 초기화되지 않기 때문에 생서자에서 초기화한다.

생성자 코드에서 멤버 변수 초기화

다음은 2개의 생성자가 각각 멤버 변수를 초기화하는 point 클래스의 사례이다.

class Point {
	int x, y;
public:
    Point();
    Point(int a, int b);
};
Point::Point() { x = 0; y = 0; }
Point::Point(int a, int b) { x = a; y = b; }

생성자 서두에 초깃값으로 초기화

이 2개의 생성자는 다음과 같이 생성자 코드의 구현부에 멤버 변수와 초깃값을 쌍으로 지정하고 이들을 콤마(,)로 나열하여 작성할 수 있다.

Point::Point () : x(0), y(0) {
}
Point::Point (int a, int b)
	: x(a), y(b) {
}

또는 다음과 같이 멤버 변수를 초기화해도 된다.

Point::Point(int a)
	: x(a), y(b) {
}
Point::Point(int a)
	: x(100+a), y(100) {
}

클래스 선언부에서 직접 초기화

멤버 변수는 C++11부터 다음과 같이 선언문에서 직접 초기화할 수 있다.

class Point {
	int x = 0, y = 0;
    
};

예제 3-5

#include <iostream>
 
class Point {
	int x, y;
public:
	Point();
	Point(int a, int b);
	void show() { std::cout << "(" << x << "," << y << ")" << std::endl;  }
};
 
Point::Point() : Point(0, 0) {} // Point(int a, int b) 생성자 호출, 위임 생성자
 
Point::Point(int a, int b) // 타겟 생성자
	:x(a), y(b) {}
 
int main() {
	Point origin;
	Point target(10, 20);
	origin.show();
	target.show();
}

생성자는 꼭 있어야 하는가?

클래스에 여러 개의 생성자가 있다 해도, C++ 컴파일러는 생성자 중 반드시 하나를 호출하도록 컴파일한다. 객체가 생성될 때 반드시 생성자 가 호출 된다면, 생성자를 선언하지 않은 클래스는 컴파일러가 기본 생성자(default constructor)를 만들어 삽입하고, 자신이 삽입한 기본 생성자를 호출하도록 컴파일한다. 

기본 생성자

기본 생성자란 클래스에 선언된 어떤 생성자도 없을 대 컴파일러가 자동으로 생성해주는 성성재로서 디폴트 생성자라고도 부르며, 다음과 같이 매개 변수 없는 생성자이다.

class Circle {
	Circle(); // 기본 생성자
};

기본 생성자가 자동으로 생성되는 경우

생성자가 하나도 없는 클래스의 경우 컴파일러는 보이지 않게 기본 생성자를 삽입한다. (a)는 생성자가 선언되지 않은 Circle 클래스를 보여준다. 하지만 main() 함수는 생성자 Circle()을 필요로 하고 있다.

Circle 클래스에 생성자가 선언되어 있지 않지만, 이 코드는 문제없이 컴파일 된다. 이것은 컴파일러에 의해 (b)와 같이 기본 생성자가 자동으로 삽입되었기 때문이다. 컴파일러가 삽입한 기본 생성자는 아무 샐행 없이 바로 리턴한다. 컴파일러가 기본 생성자를 자동으로 삽입하낟고 해서 개발자가 작성한 소스 코드가 변경되어 저장되는 것은 아니다.

기본 생성자가 자동으로 생성되지 않는 경우

생성자가 하나라도 선언된 클래스의 경우, 컴파일러는 기본 생성자를 자동 삽입하지 않는다.

donut 객체 생성을 위해 필요한 기본 생성자 Circle()은 Circle 클래스에 없다. Circle 클래스에 이미 하나의 생성자가 선언되어 있기 때문에, 컴파일러는 기본 생성자를 만들어 주지 않으며, 오히려 컴파일 오류를 발생시킨다.

예제 3-6

#include <iostream>

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

Rectangle::Rectangle() {
	width = height = 1;
}

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

bool Rectangle::isSquare() { // 정사각형이면 true를 리턴하는 멤버 함수
// bool은 논리값을 나타내는 C++의 기본 타입이다.
	if(width == height) return true;
    else return false;
}

int main() {
	Rectangle rect1; // Rectangle() 호출
    Rectangle rect2(3, 5); // Rectangle(int w, int h) 호출
    Rectangle rect3(3); // Rectangle(int length) 호출
    
    if(rect1.isSquare()) std::cout << "rect1은 정사각혁이다." << std::endl;
    if(rect2.isSquare()) std::cout << "rect2은 정사각혁이다." << std::endl;
    if(rect3.isSquare()) std::cout << "rect3은 정사각혁이다." << std::endl;
}

 

'전공 과목 > C++' 카테고리의 다른 글

클래스와 생성자  (0) 2019.09.12
C++의 입력과 문자열  (0) 2019.09.06
C++의 출력 cout  (0) 2019.09.03