본문 바로가기
lang/C,C++

[Class] 2. Class - 구성요소, 깊은복사 vs 얕은복사

by Wordbe 2019. 10. 9.
728x90

1. 생성자

멤버 변수 초기화

객체의 멤버 변수는 사용자나 프로그램이 일반적인 방법으로 초기화 할 수 없습니다.

private 멤버도 있으므로, 여기엔 직접 접근할 수 없기 때문입니다.

따라서, private 멤버 초기화를 위한 public 함수가 필요합니다.

이러한 초기화 함수는 객체가 생성된 후부터 사용되기 전까지 반드시 멤버의 초기화를 위해 호출되어야 합니다.

생성자(constructor)

c++에서는 객체의 생성과 동시에 멤버 변수를 초기화해주는 생성자 멤버 함수를 제공합니다.

생성자의 이름은 해당 클래스의 이름과 같습니다.

특별히, 객체를 초기화하는 방법이 여러 개 존재하는 경우, 오버로딩 규칙에 따라 여러개의 생성자를 가질 수 있습니다.

class Book{
private:
    string title_;
    int current_page_;
    double percent_;
    void set_percent();
public:
    int total_page_;
    Book(const string& title, int total_page){
        title_ = title;
        total_page_ = total_page;
        current_page_ = 0;
        set_percent();
    }
};

void Book::set_percent() {
    percent_ = (double) current_page_ / total_page_ * 100;
}

이렇게 생성자를 정의해 놓았다면,

생성자호출은 두가지 방법으로 할 수 있습니다.

Book book1("Linear Algebra", 689);

Book book1 = Book("Linear Algebra", 689);

2. 디폴트 생성자

초깃값을 명시하지 않으면, 컴파일러가 자동으로 0이나 NULL, 빈 문자열로 초기화합니다.

Book() {};

디폴트 생성자를 가지는 객체의 선언 3가지 방법

Book book1; // implicit
Book book1 = Book(); // explicit
Book *book1 = new Book; // implicit

3. 복사 생성자(Copy constructor)

얕은 복사와 깊은 복사

얕은복사는 대입연산자(=)를 통해 가능합니다.

Book book1("Linear Algebra", 689);
Book book2 = book1;

얕은 복사(shallow copy)란 값을 복사하는 것이 아닌, 값을 가리키는 포인터를 복사하는 것입니다.

하지만 문제점이 있는데,

예를 들면 객체의 멤버가 힙 메모리 공간을 참조할 경우에 이 방식이 문제가 생깁니다.

위와 같이 정의된 상황에서

book2 가 객체 수명이 끝날 때, 만약 book2에 멤버변수중 동적할당된 메모리(즉, 힙 영역사용)가 있다면, 그 멤버 변수는 소멸자에서 주소가 사라질 것입니다.

그렇다면 book1은 수명이 안 끝났음에도 불구하고, 그 멤버변수를 가리키고 있던 포인터가 사라짐으로써 원래 정보를 잃게 되는 문제점을 가지게 되는 것입니다.

깊은 복사(Deep copy)

class Book{
private:
    ...
    string author;
public:
    Book(const string& author_name) { // 디폴트 생성자
        author = new string;
    }; 
    Book(const Book& origin) { // 깊은 복사 생성자의 선언
        title_ = origin.title_;
        total_page_ = origin.total_page_;
        current_page_ = origin.current_page_;
        percent_ = origin.percent_;
    }
};
Book book1;
Book book2(book1);

깊은 복사로 위의 문제점을 해결할 수 있습니다.

4. 소멸자(Destructor)

객체의 수명이 끝나면 생성자의 반대역할을 수행할 멤버 함수가 필요합니다.

이 역할은 소멸자가 담당하며, 소멸자는 객체의 수명이 끝나면 컴파일러에 의해 자동으로 호출됩니다.

소멸자의 이름은 클래스의 이름과 같으며, 이름앞에 물결표시(tilde, ~)를 붙입니다.

~Book() {};

예를 들어 생성자에서 new 키워드를 통해 동적메모리를 할당했다면,

소멸자에서 메모리 누수(memory leak)를 방지하기 위해 delete 키워드를 통해 메모리를 반환해야 할 것입니다.

소멸자의 호출시기는 컴파일러가 알아서 처리하며,

객체가 선언된 메모리 영역별로 소멸자가 호출되는 시기는 다음과 같습니다.

객체가 호출된 메모리영역 소멸자 호출시기
데이터 영역 해당 프로그램이 종료될 때
스택 영역 해당 객체가 정의된 블록을 벗어날 때
힙 영역 delete를 사용하여 해당 객체 메모리를 반환할 때
임시 객체 임시 객체의 사용을 마쳤을 때

[출처 - TOPSCHOOL.com http://tcpschool.com/cpp]

728x90

댓글