가티있는블로그

[C++] 복사생성자

2021. 8. 19. 13:40 | 프로그래밍/C++

자신과 동일한 타입 한 개를 인자로 가지는 생성자.

 

사용자가 복사생성자를 만들지 않으면 컴파일러가 디폴트 복사생성자를 제공한다.

모든 멤버를 복사한다. 

디폴트의 경우는 안의 내용없이 모든 멤버를 복사한다.

 

class Point
{
public:
    int x;
    int y;

    Point()             : x(0), y(0) {}
    Point(int a, int b) : x(a), y(b) {}
    
    // 사용자가 만드는 복사 생성자
    Point( const Point& p) : x(p.y), y(p.x)
    {
        std::cout << "copy ctor" << std::endl;
    }
};

 

복사생성자가 호출되는 3가지 경우

1. 자신과 동일한 타입의 객체로 초기화 될 때

2. 함수 인자를 call by value로 받을 경우

.- 함수인자를 const reference로 사용하면 복사본을 만들지 않으므로 복사생성자가 호출되지 않는다.

3. 함수가 객체를 값으로 반환 할 때 

 - 참조리턴을 하게되면 복사생성자가 호출되지 않는다. (리턴값이 Point&)

 +) 지역변수는 참조로 반환하면 안된다

 

디폴트 복사 생성자 문제점

디폴트 복사 생성자는 모든 멤버를 복사해주고 이 점이 문제가 될 수 있다.

클래스안에 포인트 멤버가 있으면 디폴트 복사 생성자가 주소만 복사되기 때문에 자원해제시 문제가 된다.

 

얕은복사

- 클래스 안에 포인터 멤버가 있을 때 디폴트 복사 생성자가 메모리 자체를 복사하지 않고 주소만 복사하는 현상

- 개발자가 직접 복사 생성자를 만들어야한다.

 

 

객체의 복사방법: 깊은 복사

class Person
{
    char* name;
    int   age;
public:

    Person(const Person& p) : age(p.age)
    {
        // 포인터는 복사 하지말고. 새롭게 메모리 할당
        name = new char[strlen(p.name) + 1];
        strcpy(name, p.name);        
    }
};

하지만 큰 크기의 깊은 복사를 많이 하게되면 메모리 낭비가 될 수 있다.

 

 

객체의 복사 방법: 참조계수(reference counting)

여러객체가 하나의 자원을 공유 하게 한다. 단 몇명의 객체가 자원을 사용하는지 개수를 관리한다.

 

class Person
{
    char* name;
    int   age;
    int*  ref;
public:
    Person(const char* n, int a) : age(a)
    {
        name = new char[strlen(n) + 1];
        strcpy(name, n);
        
        ref = new int(1);
        //*ref = 1;
    }
    
    
    ~Person() 
    {
        // 참조 계수 기반인 경우의 소멸자.
        if ( --(*ref) == 0 )
        {
            delete[] name;
            delete ref;
        }
    }




    Person(const Person& p) 
                : name(p.name), age(p.age), ref(p.ref)
    {
        ++(*ref);
    }
    
    
};

 

공유하는 객체 중 하나가 자원을 바뀌게 되는 코드가 등장하게되면 공유했던 자원은 분리되어야하고 추가코드가 들어가게된다.

멀티 스레드 환경에서는 동기화의 오버헤드가 추가된다.

 

 

객체의 복사 방법: 복사금지

class Person
{
	...  
    Person(const Person&) = delete;
};

객체를 복사하지 못하게 하자는 의도. 복사 생성자를 delete 한다.

 

 

객체의 복사 방법: 문자열이 필요하면 STL의 string클래스를 사용

#include <string>

class Person
{
    std::string name;
    int   age;
public:
    Person(std::string n, int a) : name(n), age(a)
    {
    }
};

동적메모리 할당을 할 필요가 없다.

string이 내부적으로 자원을 관리해주고, int변수처럼 사용이 가능하다.