/*
 http://sosal.tistory.com/
 * made by so_Sal
 */

복사생성자에 관한 문제를 보고 정리해볼겸 트랙백 날립니다 ^-^


C++에는 2가지 종류의 변수 초기화 방법을 제공합니다.

int main(){
    int a = 10;   <-- C   스타일 초기화
    int b(10);    <-- C++스타일 초기화
    cout<<a<<endl<<b<<endl;
    return 0;
}

a와 b 모두 10이란 값을 출력합니다.
그럼 변수를 이렇게 두 가지 형태로 초기화가 가능하다면,
객체도 두 가지 형태로 가능하지 않을까? 란 생각을 할 수 있는데요,

#include<iostream>
using namespace std;

class data{
private:
    int value;
public:
    data(int a){
        value=a;  //간단한 생성자
    }
    void output(){
        cout<<value<<endl;
    }
};

int main(){
    data a(10);
    data b=10;
   
    a.output();
    b.output();
    return 0;
}

실행 결과 모두 10을 출력합니다.
이 실행결과를 통해서, C, C++ 초기화 스타일 모두 가능하다는 것을 알 수 있습니다.
하지만, 객체 생성에서의 C 스타일은 (data b=10;) 묵시적으로 컴파일 과정에서
data b(10); 으로 변환이 이뤄지는 것이기 때문에, 동일하다고 볼 수는 없습니다.


디폴드 복사생성자와, 얕은 복사

#include<iostream>
#include<cstring>
using namespace std;

class data{
private:
    char value[100];
public:
    data(char *val){            //생성자는 char* 자료형만 받는다.
        strcpy(value,val);      //하지만 메인에서 data b를 선언하는곳을 보면
    }
    void output(){
        cout<<value<<endl;
    }
};

int main(){
    data a("somebody help me");
    data b(a);   //char* 자료형을 넣은것이 아니라, data 객체를 넣었다.

    a.output();
    b.output();

    return 0;
}

data 객체는 char* 자료형과 분명히 다른것인데, 어떻게 이게 가능할까?
data b(a)에서 객체 생성이 성공했다는 것은, data 자료형을 받는 생성자 함수가
자동으로 삽입되었다고 생각할 수 밖에 없다. 하지만 분명히 그런 생성자 함수를 만들지 않았다.
우리는 이런 자기 자신과 같은 형태의 객체를 인자로 받을 수 있는 생성자가
자동으로 만들어진것을 디폴트 복사 생성자라 한다.
(자기 자신과 같은 형태의 객체를 받는것을 복사 생성자라고 한다.)

즉 디폴트 복사 생성자의 기능은, 멤버 대 멤버를 복사하는 것인데,
클레스마다 가지고 있는 데이터들이 다를것이기 때문에,
디폴트 생성자는 각 클레스마다 다른 형태일 것이다. 라고 예측할 수 있다.

하지만, 편리한 디폴트 생성자에게는 아주 큰 문제점이 존재한다.

#include<iostream>
#include<cstring>
using namespace std;

class data{
private:
    char *name;     //객체의 멤버변수를 참조하는 형식으로 만들어보자
    char *phone; 
    int age;
public:
    data(char *_name, char* _phone, int _age){
        name = new char[strlen(_name)+1];
        strcpy(name,_name);
        phone = new char[strlen(_phone)+1];
        strcpy(phone,_phone);
        age = _age;
    }
    void output(){
        cout<<name<<endl;
        cout<<phone<<endl;
        cout<<age<<endl;
    }
    void modify(char *val){
        strcpy(name,val);
    }
    ~data(){
        delete []name;
        delete []phone;
    }  //소멸자에서 멤버변수 다 없애줘야겠죠?
};

int main(){
    data *a = new data("sosal","010-6450-7939",21);
    data b(*a);


    a->output();
    a->modify("aaa");

    b.output();
    delete a;
    b.output();
    return 0;
}

 
위 문제의 소스를 제대로 분석해보면
엄청나게 큰 문제점을 찾을 수 있다.

말그대로, 디폴트 복사 생성자는
직접 값을 복사하는것이 아니라
"참조" 하고 있던것이다.

이를 얕은 복사라 하며,
객체가 실제로 데이터를 가지고 있는것이
아님을 알 수 있다.






와.. 이해 진짜 잘된다. 박수한번 치자. 짞짞짜까ㅉ까짜까ㅉ까짞짜까짜까


그럼, 디폴트 복사생성자가 수행하는 얕은복사 말고, 깊은 복사를 해보자.
깊은 복사를 하기 위해선, 우리가 직접 복사생성자를 정의할것이다.

#include<iostream>
#include<cstring>
using namespace std;

class data{
private:
    char *name;
    char *phone;
    int age;
public:
    data(char *_name, char* _phone, int _age){
        name = new char[strlen(_name)+1];
        strcpy(name,_name);
        phone = new char[strlen(_phone)+1];
        strcpy(phone,_phone);
        age = _age;
    }
    data(const data& object){
        name = new char[strlen(object.name)+1];
        strcpy(name,object.name);
        
// 이경우, private 멤버변수도 접근이 가능하므로, 되도록 매개변수를 const로 설정
        phone = new char[strlen(object.phone)+1];
        strcpy(phone,object.phone);
       
        age = object.age;
    }
    void output(){
        cout<<name<<endl;
        cout<<phone<<endl;
        cout<<age<<endl;
    }
    void modify(char *val){
        strcpy(name,val);
    }
    ~data(){
        delete []name;
        delete []phone;
    }
};

int main(){
    data *a = new data("sosal","010-6450-7939",21);


    data b(*a);

    a->output();
    delete a;
    b.output();
}






* 참고자료 : 열혈강의 C++ 윤성우 저

Posted by sosal sosal

댓글을 달아 주세요

  1. 2010.10.03 00:57 신고

    좋은정보 감사합니다^^

  2. 2011.08.03 14:59 신고

    잘 보고 갑니다 ^^