C++

복사 생성자

Barbarian developer 2024. 9. 27.

복사 생성자, 언제 쓰일까?

  • 새로운 객체를 만들 때: 기존 객체와 똑같은 내용을 가진 새로운 객체를 만들고 싶을 때 사용합니다.
  • 객체를 함수에 전달할 때: 함수에 객체를 값으로 전달하면, 복사 생성자를 통해 함수 내부에서 사용할 복사본을 만듭니다.
  • 객체를 함수에서 반환할 때: 함수가 객체를 반환하면, 복사 생성자를 통해 반환할 복사본을 만듭니다.
#include <iostream>
using namespace std;

class Person 
{
public:
    string name;  // 이름을 저장하는 멤버 변수
    int age;      // 나이를 저장하는 멤버 변수

    // 복사 생성자: 다른 Person 객체의 데이터를 복사하여 새로운 객체를 초기화
    Person(const Person& other) 
    {
        name = other.name;  // 다른 객체의 name 값을 복사
        age = other.age;    // 다른 객체의 age 값을 복사
    }

    // 매개변수가 있는 생성자 (추가)
    Person(string myname, int myage) 
    {
        name = myname;
        age = myage;
    }
};

int main() 
{
    // 매개변수가 있는 생성자를 사용하여 Person 객체 생성
    Person original("Alice", 25);
    // 복사 생성자를 사용하여 새로운 객체 copy를 original로부터 생성
    Person copy(original); 
    // 복사된 객체의 이름과 나이 출력 (Alice 25 출력)
    cout << copy.name << " " << copy.age << endl;

    return 0;
}

//출력
Alice 25
 
 

얕은 복사& 깊은 복사

  • 얕은 복사: 간단하고 빠르지만, 두 객체가 같은 메모리를 공유하기 때문에 한 객체에서 메모리를 변경하면 다른 객체에도 영향을 미칠 수 있어요. 특히 동적 메모리 할당을 사용하는 경우 문제가 발생할 수 있습니다.
  • 깊은 복사: 두 객체가 완전히 독립적이기 때문에 안전하지만, 메모리 복사에 시간이 더 걸리고 복잡한 구현이 필요할 수 있습니다.

얕은 복사와 깊은 복사의 활용 시점

  • 얕은 복사: 객체의 멤버 변수가 모두 기본 자료형(int, float 등)이거나 동적 메모리 할당을 사용하지 않는 경우에 적합합니다.
  • 깊은 복사: 객체의 멤버 변수 중에 동적 메모리 할당을 사용하는 포인터가 있는 경우에는 깊은 복사를 사용해야 합니다.
Person man1("Lee dong wll", 29);
Person man2=man1;	//복사 생성자 호출

복사 생성자와 동적 메모리 할당:

  • 만약 클래스의 멤버 변수 중에 동적으로 할당된 메모리를 가리키는 포인터가 있다면, 얕은 복사를 사용하는 복사 생성자는 문제를 일으킬 수 있습니다.
  • 얕은 복사는 포인터 값만 복사하기 때문에, 원본 객체와 복사된 객체가 같은 메모리 영역을 가리키게 됩니다. 이 경우 한 객체에서 메모리를 변경하면 다른 객체에도 영향을 미치거나, 객체가 소멸될 때 같은 메모리 영역을 두 번 해제하려고 하는 문제가 발생할 수 있습니다.

 

깊은 복사

  • 독립적인 메모리 공간 확보
  • 이러한 문제를 해결하기 위해 깊은 복사를 사용해야 합니다. 깊은 복사는 동적으로 할당된 메모리까지 복사하여 완전히 독립적인 새로운 객체를 만듭니다. 즉, 원본 객체와 복사된 객체는 각자 독립적인 메모리 공간을 가지게 되므로, 한 객체에서 메모리를 변경해도 다른 객체에 영향을 미치지 않습니다.

 

깊은 복사를 구현하려면?

  • 깊은 복사를 구현하려면 복사 생성자 내부에서 new 연산자를 사용하여 새로운 메모리 공간을 할당하고, 기존 객체의 데이터를 복사해야 합니다.
#include <iostream>

using namespace std;

// SoSimple 클래스 정의
class SoSimple
{
private:
    int num;  // 멤버 변수 num

public:
    // 생성자: 매개변수 n을 받아 num을 초기화
    SoSimple(int n) : num(n)
    { }

    // 복사 생성자: 객체가 복사될 때 호출됨
    SoSimple(const SoSimple& copy) : num(copy.num)
    {
        // 복사 생성자가 호출될 때 출력
        cout << "Called SoSimple(const SoSimple& copy)" << endl;
    }

    // AddNum 함수: num 값에 n을 더하고, 자기 자신을 반환 (메서드 체이닝 가능)
    SoSimple& AddNum(int n)
    {
        num += n;   // num에 n을 더함
        return *this;  // 자기 자신을 반환
    }

    // ShowData 함수: num 값을 출력하는 함수
    void ShowData()
    {
        cout << "num: " << num << endl;  // num 값 출력
    }
};

// SoSimple 객체를 매개변수로 받아서 처리하는 함수
SoSimple SimpleFuncObj(SoSimple ob)  // 값으로 전달됨 (복사본이 생성됨)
{
    cout << "return 이전" << endl;   // 반환 이전에 출력
    return ob;  // 매개변수로 받은 ob 객체를 반환 (여기서 복사된 객체 반환)
}

int main()
{
    // SoSimple 객체 생성, num을 7로 초기화
    SoSimple obj(7);

    cout << "함수호출 전" << endl;  // 함수 호출 전 출력

    // SimpleFuncObj 함수 호출: obj의 복사본이 생성됨
    // 반환된 복사본에 대해 AddNum(30)을 호출하여 num에 30을 더하고 ShowData로 출력
    SimpleFuncObj(obj).AddNum(30).ShowData();  // obj의 복사본에서 num 값이 37이 됨

    // 원래 obj 객체의 num 값을 출력 (함수 호출로 인해 변화 없음)
    obj.ShowData();  // 원래 obj의 num 값은 여전히 7임

    return 0;
}

 

'C++' 카테고리의 다른 글

this 포인터의 이해  (0) 2024.09.26
' : '과 ' :: '의 차이점  (0) 2024.09.26
c++클래스(2)  (0) 2024.09.25
c++ 참조  (0) 2024.09.25
c++ 클래스  (3) 2024.09.25

댓글