뚜둔뚜둔✩

판다의 개발일지

C · C++

[C++] 복사 생성자 (Copy Constructor)

2023. 1. 15. 20:42

    모든 객체는 생성 시 "생성자" 함수를 자동으로 호출한다.

     

    [C++] 구조체(struct)와 클래스(class)

    구조체 여러 데이터 값을 갖는 어떤 새로운 타입을 정의할 때 흔히 구조체를 사용한다. C 언어에서의 구조체는 실제 데이터만을 포함하지만 C++에서는 데이터뿐만 아니라 생성자, 소멸자, 멤버

    pandas-are-bears.tistory.com


    컴파일러와 생성자

    생성자는 반드시 생성 시에 호출되기 때문에, 사용자가 생성자를 별도로 선언하지 않아도 컴파일러가 아무 동작을 하지 않는 생성자를 자동으로 생성하여 추가한다. 다음 예시에서 볼 수 있듯이, 컴파일러가 자동 생성하는 생성자 덕분에 p1 선언에는 문제가 없지만 p2 선언은 컴파일 에러가 발생한다.

    class Point
    {
    private:
    	int x;
    	int y;
    };
    
    int main(void)
    {
    	Point p1;
    	Point p2(1, 2);
        
        return 0;
    }
    test.cpp:11:8: error: no matching constructor for initialization of 'Point'
            Point p2(1, 2);
                  ^  ~~~~
    test.cpp:1:7: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 2 were provided
    class Point
          ^
    test.cpp:1:7: note: candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 2 were provided
    1 error generated.

    에러 메세지를 좀 더 구체적으로 살펴보자.

    아무 생성자가 제공되지 않았으나, 컴파일러는 2개의 생성자가 있음을 나타내고 있다.

    • default constructor (디폴트 생성자)   →  0개 argument
    • copy constructor (복사 생성자)          →  1개 argument

     

    디폴트 생성자

    여기서 디폴트 생성자가 바로 컴파일러가 자동으로 생성하는, 아무 동작을 하지 않는 일반 생성자이다.

    Point::Point() { }

    복사 생성자

    그렇다면, 1개의 argument를 요구하는 복사 생성자는 무엇일까?

    복사 생성자는 바로 생성자를 호출하는 객체와 동일한 타입의 객체를 인자로 전달받아, 전달받은 객체와 동일하게 객체를 구성(=복사)하는 생성자이다.

    Point::Point(const Point& obj)
    {
    	x = obj.x;
        y = obj.y;
    }

    이렇게 복사 생성자가 자동으로 생성되기 때문에 다음과 같은 코드도 문제 없이 동작할 수 있다.

    class Point
    {
    public:
    	Point(int x_val, int y_val) : x(x_val), y(y_val) { }
    	void print(std::string str)
    	{
    		std::cout << str << " : " << x << ", " << y << std::endl;
    	}
    private:
    	int x;
    	int y;
    };
    
    int main(void)
    {
    	Point p1(12, 34);
    	p1.print("p1");
    
    	Point p2(p1);		// no error
    	p2.print("p2");
        
        return 0;
    }
    p1 : 12, 34
    p2 : 12, 34

    실행 결과를 확인해보면 복사 생성자를 호출했을 p2가 인자로 받은 p1을 그대로 복사하였음을 볼 수 있다.

     

    복사 생성자의 특징

    • 별도의 복사 생성자가 제공되지 않으면 컴파일러가 자동으로 복사 생성자를 제공한다.
    • 복사 생성자가 아닌 형태(= 동일 타입의 객체를 인자로 받는)의 일반 생성자가 제공되더라도 컴파일러는 제공된 복사 생성자가 없다면 자동으로 생성하여 복사 생성자를 제공한다.
    • 별도의 복사 생성자가 제공된다면 컴파일러는 디폴트 생성자를 제공하지 않기 때문에 일반 생성자가 제공되어야 한다.
    #include <iostream>
    #include <string>
    
    class Point
    {
    public:
    	Point(const Point &p) : x(p.x), y(p.y) { }
    private:
    	int x;
    	int y;
    };
    
    int main(void)
    {
    	Point p1;		// error
    }
    test.cpp:15:8: error: no matching constructor for initialization of 'Point'
            Point p1;
                  ^
    test.cpp:7:2: note: candidate constructor not viable: requires single argument 'p', but no arguments were provided
            Point(const Point &p) : x(p.x), y(p.y) { }
            ^
    1 error generated.

     

    복사 생성자는 언제 호출될까?

    1. 복사 생성자는 어떤 객체에 대한 대입 및 초기화가 이뤄질 때 호출된다.
    2. 어떤 객체를 함수의 인자로 전달할 때 복사 생성자 호출을 통한 복사본이 생성된다.
    3. 함수가 객체를 리턴하게 될 때 리턴을 위한 임시 객체가 생성되므로 복사 생성자가 호출된다.
     

    [C++] 초기화 (Initialization)

    대입과 초기화 대입 (Assignment) 대입은 = 대입 연산자를 사용하여 어떤 변수에 값을 지정하는 것이다. 이를 "copy assignment"라고도 하는데, 그 이유는 대입은 등호 왼쪽의 객체(변수)에 등호 오른쪽의

    pandas-are-bears.tistory.com