Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C++ 가상 복사 생성자(Virtual Copy Constructor)의 개념과 활용법

C++에서 객체 지향 프로그래밍을 깊이 있게 이해하기 위해, 본격적인 주제로 들어가기 전에 관련된 핵심 용어들을 먼저 정리해 보겠습니다.

핵심 개념 정리

복사 생성자(Copy Constructor)

복사 생성자는 전달받은 객체와 완전히 동일한 복제본을 만들어 내는 특수한 생성자입니다. 같은 클래스 타입의 다른 객체를 인자로 받아, 그 객체의 멤버 값들을 그대로 복사하여 새로운 객체를 초기화하는 역할을 수행합니다.

가상 함수(Virtual Function)

가상 함수는 부모(기반) 클래스에서 선언되고, 이를 상속받는 자식(파생) 클래스에서 재정의(오버라이딩)되는 멤버 함수입니다. virtual 키워드로 선언되며, 컴파일 시점이 아닌 런타임에 실제 객체의 타입에 따라 호출될 함수가 결정됩니다.

가상 복사 생성자가 필요한 이유

가상 복사 생성자를 활용하면 프로그래머는 객체의 정확한 데이터 타입을 미리 알지 못하더라도 해당 객체를 손쉽게 복제할 수 있습니다.

C++에서 일반적인 복사 생성자는 다른 객체로부터 새로운 객체를 생성하는 데 사용됩니다. 하지만 생성될 객체의 타입이 컴파일 시점이 아니라 런타임에 결정되어야 하는 경우가 있습니다. 예를 들어 사용자의 입력이나 특정 조건에 따라 객체의 타입이 달라져야 한다면, 일반 복사 생성자만으로는 해결할 수 없습니다.

바로 이런 상황에서 특별한 기능을 가진 복사 생성자, 즉 가상 복사 생성자가 필요합니다. 가상 복사 생성자는 런타임에 객체를 실시간으로 복제(cloning)할 수 있는 능력을 제공합니다.

예시로 이해하기

프로그램을 통해 도형의 넓이를 구한다고 가정해 보겠습니다. 그런데 객체의 타입은 실행 중에 결정되며, 정사각형일 수도 있고 직사각형 또는 원일 수도 있습니다. 이럴 때 가상 복사 생성자를 사용하면 사용자가 입력한 타입에 맞춰 객체를 자동으로 복제할 수 있습니다.

clone()과 create() 메서드

가상 생성자가 올바르게 동작하려면 기반 클래스에 두 가지 메서드가 정의되어야 합니다.

clone()
create()

복사 생성자는 가상 clone() 메서드를 사용하고, 가상 create() 메서드는 기본 생성자가 가상 생성자를 만들 때 사용됩니다. 즉, create()는 런타임 입력에 따라 적절한 객체를 새로 생성하고, clone()은 이미 존재하는 객체의 복제본을 만드는 역할을 담당합니다.

예제 코드

아래 예제는 도형(figure) 기반 클래스와 이를 상속받는 square, circle, rectangle 클래스를 통해 가상 복사 생성자의 동작 방식을 보여줍니다.

#include <iostream>
using namespace std;
class figure{
    public:
    figure() { }
    virtual
    ~figure() { }
    virtual void ChangeAttributes() = 0;
    static figure *Create(int id);
    virtual figure *Clone() = 0;
};
class square : public figure{
    public:
    square(){
        cout << "square created" << endl;
    }
    square(const square& rhs) { }
    ~square() { }
    void ChangeAttributes(){
        int a;
        cout<<"The side of square";
        cin>>a;
        cout<<"Area of square is "<<a*a;
    }
    figure *Clone(){
        return new square(*this);
    }
};
class circle : public figure{
    public:
    circle(){
        cout << "circle created" << endl;
    }
    circle(const circle& rhs) { }
    ~circle() { }
    void ChangeAttributes(){
        int r;
        cout << "enter the radius of the circle ";
        cin>>r;
        cout<<"the area of circle is "<<((3.14)*r*r);
    }
    figure *Clone(){
        return new circle(*this);
    }
};
class rectangle : public figure{
    public:
    rectangle(){
        cout << "rectangle created" << endl;
    }
    rectangle(const rectangle& rhs) { }
    ~rectangle() { }
    void ChangeAttributes(){
        int a ,b;
        cout<<"The dimensions of rectangle ";
        cin>>a>>b;
        cout<<"Area of rectangle is "<<a*b;
    }
    figure*Clone(){
        return new rectangle(*this);
    }
};
figure *figure::Create(int id){
    if( id == 1 ){
        return new square;
    }
    else if( id == 2 ){
        return new circle;
    }
    else{
        return new rectangle;
    }
}
class User{
    public:
    User() : figures(0){
        int input;
        cout << "Enter ID (1, 2 or 3): ";
        cin >> input;
        while( (input != 1) && (input != 2) && (input != 3) ){
            cout << "Enter ID (1, 2 or 3 only): ";
            cin >> input;
        }
        figures = figure::Create(input);
    }
    ~User(){
        if( figures ){
            delete figures;
            figures = 0;
        }
    }
    void Action(){
        figure *newfigure = figures->Clone();
        newfigure->ChangeAttributes();
        delete newfigure;
    }
    private:
    figure *figures;
};
int main(){
    User *user = new User();
    user->Action();
    delete user;
}

실행 결과

사용자가 ID로 2를 입력하면 circle 객체가 생성되고, Clone() 메서드를 통해 복제된 객체에서 반지름을 입력받아 넓이를 계산합니다.

Enter ID (1, 2 or 3): 2
circle created
enter the radius of the circle R 3
the area of circle is 28.26

마무리

가상 복사 생성자는 C++에서 런타임에 객체 타입이 결정되는 상황에서 매우 유용한 패턴입니다. 기반 클래스의 Create() 메서드로 사용자 입력에 따라 적절한 파생 클래스 객체를 생성하고, Clone() 메서드로 해당 객체의 복제본을 만들어 활용함으로써, 컴파일 시점에는 알 수 없는 타입의 객체도 안전하게 다룰 수 있습니다. 이러한 기법은 팩토리 패턴 및 프로토타입 패턴과도 밀접한 관련이 있으므로 함께 학습하면 더욱 효과적입니다.