본문 바로가기
Programming/C++

C++ 얕은복사 깊은복사

by ahhang0k 2021. 4. 21.

얕은 복사(Shallow Copy)

얕은 복사는 객체가 가진 멤버들의 값을 새로운 객체로 복사하는데 만약 객체가 참조타입의 멤버를 가지고 있다면 참조값만 복사가 됩니다.



깊은 복사 (Deep Copy)

깊은 복사는 전체 복사라고도 합니다.

얕은 복사와는 달리 객체가 가진 모든 멤버(값과 참조형식 모두)를 복사하는 것을 말합니다.

객체가 참조 타입의 멤버를 포함할 경우 참조값의 복사가 아닌 참조된 객체 자체가 복사되는 것을 깊은 복사라 합니다



※ 얕은복사 깊은복사 총정리

얕은 복사 경우 동적 할당을 받은 변수의 주소값을 공유한다. 깊은 복사 경우에는 새로이 동적 할당은 받고, 원본의 데이터까지 복사 한다.

 

 

복사생성자

객체의 복사가 이루어질  호출되는 생성자이다.
클래스 내에서 일반 생성자는 파라미터를 다르게 하여서 여러개를   있지만, 복사 생성자는 파라미터를
자기 클래스 타입의 참조 변수 하나만 가지며, 때문에 하나의 복사 생성자만 존재할  있다.

 

 

 

ex 1) 

#include <iostream>
#include <cstring>
using namespace std;
class Person { // Person 클래스 선언
	char* name;
	int id;
public:
	Person(int id, char* name); // 생성자
	~Person(); // 소멸자
	void changeName(char* name);
	void show() { cout << id << ',' << name << endl; }
};
Person::Person(int id, char* name) { // 생성자
	this->id = id;
	int len = strlen(name); // name의 문자 개수
	this->name = new char[len + 1]; // name 문자열 공갂 핟당
	strcpy(this->name, name); // name에 문자열 복사
}
Person::~Person() {// 소멸자
	if (name) // 만일 name에 동적 핛당된 배열이 있으면
		delete[] name; // 동적 핛당 메모리 소멸
}
void Person::changeName(char* name) { // 이름 변경
	if (strlen(name) > strlen(this->name))
		return;
	strcpy(this->name, name);
}

int main() {
	Person father(1, "Kitae"); // (1) father 객체 생성
	Person daughter(father); // (2) daughter 객체 복사 생성. 복사생성자호출
	cout << "daughter 객체 생성 직후 ----" << endl;
	father.show(); // (3) father 객체 출력
	daughter.show(); // (3) daughter 객체 출력
	daughter.changeName("Grace"); // (4) daughter의 이름을 "Grace"로 변경
	cout << "daughter 이름을 Grace로 변경한 후 ----" << endl;
	father.show(); // (5) father 객체 출력
	daughter.show(); // (5) daughter 객체 출력
	return 0; // (6), (7) daughter, father 객체 소멸
}

 

에러가 발생합니다. -> 이유는? 

얕은복사로 인해 daughter객체가 father의 주소값을 참조 했지만 소멸자가 daughter를 소멸하고 다음인 father을 소멸하려고 보니 이미 소멸자가 한번생성이되었고 또다시 호출이 되어서 에러가 발생합니다! 

 

#include <iostream>
#include <cstring>

using namespace std;
class Person { // Person 클래스 선언
	char* name;
	int id;
public:
	Person(int id, const char* name); // 생성자
	Person(Person& person);
	~Person(); // 소멸자
	void changeName(const char* name);
	void show() { cout << id << ',' << name << endl; }
};

//문자열길이 받아서 동적할당
Person::Person(int id, const char* name) { // 생성자
	this->id = id;
	int len = strlen(name); // name의 문자 개수
	this->name = new char[len + 1]; // name 문자열 공갂 핟당
	strcpy_s(this->name,len+1,name); // name에 문자열 복사
}

Person::Person(Person& person) {
	this->id = person.id;
	int len = strlen(person.name);
	this->name = new char[strlen(person.name) + 1];  //name을 위한 공간 할당
	strcpy_s(this->name,len+1, person.name);
	cout << "복사 생성자 실행, 원본 객체의 이름 " << this->name << endl;

}

Person::~Person() {// 소멸자
	if (name) // 만일 name에 동적 핛당된 배열이 있으면
		delete[] name; // 동적 할당 메모리 소멸
}

void Person::changeName(const char* name) { // 이름 변경
	if (strlen(name) > strlen(this->name))
		return;
	strcpy_s(this->name,strlen(name)+1,name);
}

int main() {
	Person father(1, "Kitae");
	Person daughter(father);

	cout << "daughter 객체 생성 직후----" << endl;
	father.show();
	daughter.show();

	daughter.changeName("Grace");
	cout << "daughter 이름을 Grace로 변경한 후------" << endl;
	father.show();
	daughter.show();

	return 0;
}

 

 

또다른 방식

#define _CRT_SECURE_NO_WARNINGS //비주얼 스튜디오에서 strcpy로 인한 오류를 막기 위한 선언문
#include <iostream>
#include <cstring>
using namespace std;

class Person { // Person 클래스 선언
    char* name;
    int id;
public:
    Person(int id, const char* name); // 생성자
    Person(Person& person); // 복사 생성자
    ~Person(); // 소멸자
    void changeName(const char* name);
    void show() { cout << id << ',' << name << endl; }
};

Person::Person(int id, const char* name) { // 생성자
    this->id = id;
    int len = strlen(name); // name의 문자 개수
    this->name = new char[len + 1]; // name 문자열 공간 핟당
    strcpy(this->name, name); // name에 문자열 복사
}

Person::Person(Person& person) { // 복사 생성자
    this->id = person.id; // id 값 복사
    int len = strlen(person.name);// name의 문자 개수
    this->name = new char[len + 1]; // name을 위한 공간 핟당
    strcpy(this->name, person.name); // name의 문자열 복사
    cout << "복사 생성자 실행. 원본 객체의 이름 " << this->name << endl;
}

Person::~Person() {// 소멸자
    if (name) // 만일 name에 동적 할당된 배열이 있으면
        delete[] name; // 동적 할당 메모리 소멸
}

void Person::changeName(const char* name) { // 이름 변경
    if (strlen(name) > strlen(this->name))
        return; // 현재 name에 할당된 메모리보다 긴 이름으로 바꿀 수 없다.
    strcpy(this->name, name);
}

int main() {
    Person father(1, "Kitae");            // (1) father 객체 생성
    Person daughter(father);            // (2) daughter 객체 복사 생성. 복사 생성자 호출

    cout << "daughter 객체 생성 직후 ----" << endl;
    father.show();                        // (3) father 객체 출력
    daughter.show();                    // (3) daughter 객체 출력

    daughter.changeName("Grace"); // (4)     // daughter의 이름을 "Grace"로 변경
    cout << "daughter 이름을 Grace로 변경한 후 ----" << endl;
    father.show();                        // (5) father 객체 출력
    daughter.show();                    // (5) daughter 객체 출력

    return 0;                                // (6), (7) daughter, father 객체 소멸
}

 

 

 

ex02)

 

책의 이름과 가격을 저장하는 다음과 같은 Book 클래스를 작성하시오.

class Book{

char *title;

int price;

public:

Book(const char* title, int price);

~Book();

void set(char* title, int price);

void show(){cout<< title << "," << price<<"원"<<endl;}

};

1)Book 클래스의 생성자, 소멸자, set() 함수를 정의하시오.

set()함수는 멤버 변수 title에 할당된 메모리가 있으면 먼저 반화하고 새로운 메모리를 할당받고

이곳에 매개 변수로 전달받은 책 이름을 저장하라.

2) 디폴트 복사 생성자를 호출하여 생기는 문제를 해결하시오.

int main(){

Book cpp("c++", 10000);

Book java = cpp;

java.set("java", 12000);

cpp.show();

java.show();

}

 

#include <iostream>
using namespace std;
#include <string>
class Book {
    char* title; // 제목 문자열;
    int price; // 가격
public:
    Book(const char* title, int price);
    ~Book();
    void set(char* title, int price);
    void show() { cout << title << ' ' << price << "원" << endl; }
};
//
//Book::Book(const char* title, int price) {
//    strcpy(this->title, title);
//    this->price = price;
//}
Book::~Book() {
    delete title;
}
void Book::set(char* title, int price) {
    if (this->title)
        delete title;
    int len = strlen(title);
    this->title = new char[len + 1];
    strcpy(this->title, title);
}

Book::Book(const char* title, int price) {
    int len = strlen(title);
    this->title = new char[len + 1];
    strcpy(this->title, title);
    this->price = price;
}


int main() {
    Book cpp("c++", 10000);
    Book java = cpp;
    java.set("java", 12000);
    cpp.show();
    java.show();

}

 

C2664 오류 발생!!

 

현재 Visual Studio 2017버전 이상부턴 표준문법을 엄격히 사용하도록 하게 되어있습니다.

때문에 permissive- 모드로 기본 설정되어 있습니다.

 

Visual Studio는 문자열 저장방식을 (char *)이 아닌 string사용을 권장하고 있습니다.

 

때문에 전 버전을 사용하는 Visual Studio는 상관이 없지만 2017이상 버전을 사용하시는 분들은 

아래와 같은 Error메시지를 볼 수 있습니다.

 

 

실행 오류가 발생하는 원인은 Book 클래스에서 C-스트링(char* title) 방식으로 문자열을 다루었기 때문이다. 그래서 복사 생성자를 작성하지 말고 문자열을 string 클래스를 사용하여, 실행 오류가 발생하지 않도록 Book 클래스를 수정함.

 

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

class Book {
    string title; 
    int price;
public:
    Book(string title, int price);
    void set(string title, int price);
    void show() { cout << title << ' ' << price << "원" << endl; }
};

Book::Book(string title, int price) {
    this->title = title;
    this->price = price;
}

void Book::set(string title, int price) {
    this->title = title;
    this->price = price;
}

int main() {
    Book cpp("c++", 10000);
    Book java = cpp;
    java.set("java", 12000);
    cpp.show();
    java.show();

    return 0;
}

 

 

 

 

참고

frenchkebab.tistory.com/9

 

#5-B 복사 생성자

|5.5| 복사 생성자 ● 얕은 복사와 깊은 복사 (shallow copy & deep copy) Person 객체의 멤버 변수로 int id; char *name; 이 있다고 가정하자. 얕은 복사의 경우, name 포인터로 문자열 배열을 공유하기 때문에..

frenchkebab.tistory.com

numerok.tistory.com/69

 

명품 C++ 5장 11번

11. 책의 이름과 가격을 저장하는 다음 Book 클래스에 대해 물음에 답하여라. #include using namespace std; #include class Book {     char *title; // 제목 문자열;     int price; // 가격 public:..

numerok.tistory.com

hwan-shell.tistory.com/29

 

C++의 생성자, 문자열 초기화 C2664 오류시 해결!!

C++에서 클래스를 생성하면 생성자가 자동으로 생깁니다. 여기서 생성자란!! 객체선언시 자동으로 실행되는 코드를 말합니다. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include using namespace std; class A {  ..

hwan-shell.tistory.com

 

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

C++ 상속 - 업캐스팅, 다운캐스팅, protected 접근지정자  (0) 2021.04.21
C++ static  (0) 2021.04.21
C++ 참조자  (0) 2021.04.20
C++ 문자열 string 실습  (0) 2021.04.20
C++ 동적할당 실습 문제  (0) 2021.04.20

댓글