생성자
멤버 변수의 초기화
클래스를 가지고 객체를 생성하면, 해당 객체는 메모리에 즉시 생성됩니다. 하지만 이 객체는 모든 멤버 변수를 초기화하기 전에는 사용할 수 없습니다. 그리고 객체의 멤버 변수는 사용자나 프로그램이 일반적인 초기화 방식으로 초기화할 수 없습니다.
왜 그럴까요?
그 이유는 객체의 멤버 중에는 private 멤버도 있으므로, 이러한 private 멤버에 직접 접근할 수 없기 때문입니다. 따라서 private 멤버에 접근할 수 있는 초기화만을 위한 public 함수가 필요합니다.
즉, '생성자'라는 것을 이용하면 객체의 생성과 동시에 멤버변수를 초기화할 수 있습니다. 객체가 되기 위해서는 반드시 하나의 생성자가 호출되어야 합니다. C++에서 클래스 생성자의 이름은 해당 클래스의 이름과 같습니다.
이러한 생성자는 다음과 같은 특징을 가집니다.
1. 생성자는 초기화를 위한 데이터를 인수로 전달받을 수 있습니다.
2. 생성자는 반환값이 없지만, void형으로 선언하지 않습니다.
3.생성자도 함수의 일종이기 때문에 함수 오버로딩이 가능합니다.
*함수 오버로딩 : 매개변수의 타입이나 개수를 달리한 이름이 같은 함수를 정의하는 것
4.생성자도 함수의 일종이기 때문에 매개변수에 '디폴트 값'을 설정할 수 있습니다.
디폴트 생성자
디폴트 생성자는 객체가 생성될 때 사용자가 초깃값을 명시하지 않으면, 컴파일러가 자동으로 제공하는 생성자입니다.
디폴트 생성자는 사용자로부터 인수를 전달받지 않으므로, 매개변수를 가지지 않습니다.
매개변수를 가지지 않으므로 대부분의 디폴트 생성자가 0이나 NULL, 빈 문자열 등으로 초기화를 진행합니다.
디폴트 생성자의 정의
C++에서 사용자가 직접 디폴트 생성자를 정의하는 방법은 다음과 같습니다.
1. 함수 오버로딩을 이용한 방법
2. 디폴트 인수를 이용한 방법
클래스는 단 하나의 디폴트 생성자만을 가질 수 있으므로, 둘 중 한 가지 방법으로만 디폴트 생성자를 정의해야 합니다.
- 디폴트 인수를 이용한 디폴트 생성자의 정의
C++에서는 기존 생성자의 모든 인수에 디폴트 인수를 명시함으로써 디폴트 생성자를 정의할 수 있습니다.
Book::Book(const string& title = "웹 프로그래밍", int total_page = "100");
위의 예제처럼 모든 인수에 디폴트 값을 명시하면, 인수를 전달하지 않고도 객체를 생성할 수 있는 디폴트 생성자가 됩니다.
- 함수 오버로딩을 이용한 디폴트 생성자의 정의
C++에서는 함수 오버로딩을 이용하여 매개변수를 가지지 않는 또 하나의 생성자를 정의할 수 있습니다.
Book();
디폴트 생성자를 가지는 객체의 선언
C++에서 디폴트 생성자를 가지는 객체는 다음과 같이 여러 가지 방법으로 선언할 수 있습니다.
1. Book web_book; // 디폴트 생성자의 암시적 호출
2. Book web_book = Book(); // 디폴트 생성자의 명시적 호출
3. Book *ptr_book = new Book; // 디폴트 생성자의 암시적 호출
하지만 다음과 같은 구문은 컴파일러가 객체의 선언이 아닌 함수의 호출로 보고 오류를 발생시킬 것입니다.
Book web_book(); (x)
복사 생성자
얕은 복사와 깊은 복사
새롭게 생성하는 변수에 다른 변수의 값을 대입하기 위해서는 대입 연산자(=)를 사용하면 됩니다.
int x = 10;
int y = x;
마찬가지로 새롭게 생성하는 객체에 또 다른 객체의 값을 대입하기 위해서도 대입 연산자(=)를 사용할 수 있습니다.
Book web_book("HTML과 CSS", 350);
Book html_book = web_book;
하지만 대입 연산자를 이용한 객체의 대입은 얕은 복사(shallow copy)로 수행됩니다.
얕은 복사(shallow copy)란 값을 복사하는 것이 아닌, 값을 가리키는 포인터를 복사하는 것입니다.
따라서 변수의 생성에서 대입 연산자를 이용한 값의 복사는 문제가 되지 않지만, 객체에서는 문제가 발생할 수도 있습니다.
특히 객체의 멤버가 메모리 공간의 힙(heap) 영역을 참조할 경우에는 문제가 발생합니다.
복사 생성자
복사 생성자란 자신과 같은 클래스 타입의 다른 객체에 대한 참조(reference)를 인수로 전달받아, 그 참조를 가지고 자신을 초기화하는 방법입니다.
복사 생성자는 새롭게 생성되는 객체가 원본 객체와 같으면서도, 완전한 독립성을 가지게 해줍니다.
왜냐하면, 복사 생성자를 이용한 대입은 깊은 복사(deep copy)를 통한 값의 복사이기 때문입니다.
Book 클래스의 복사 생성자의 원형은 다음과 같습니다.
Book(const Book&);
복사 생성자는 다음과 같은 상황에서 주로 사용됩니다.
1. 객체가 함수에 인수로 전달될 때
2. 함수가 객체를 반환값으로 반환할 때
3. 새로운 객체를 같은 클래스 타입의 기존 객체와 똑같이 초기화할 때
Book::Book(const Book& origin) // 복사 생성자의 선언
{
title_ = origin.title_;
total_page_ = origin.total_page_;
current_page_ = origin.current_page_;
percent_ = origin.percent_;
}
int main(void)
{
Book web_book("HTML과 CSS", 350);
Book html_book(web_book);
...
}
첫 번째 책의 제목은 HTML과 CSS이고, 총 페이지는 350장입니다.
두 번째 책의 제목은 HTML과 CSS이고, 총 페이지는 350장입니다.
위의 예제는 복사 생성자를 이용해 새롭게 생성되는 html_book 객체를 같은 클래스의 web_book 객체로 초기화하고 있습니다. 예제에서 Book html_book(web_book); 구문은 컴파일러에 의해 다음과 같이 복사 생성자를 사용한 것으로 해석됩니다.
Book html_book = Book(web_book);
소멸자
객체의 수명이 끝나면 생성자의 반대 역할을 수행할 멤버 함수도 필요해지는데, 이러한 역할을 하는 멤버 함수를 소멸자라고 합니다. 소멸자는 객체의 수명이 끝나면 컴파일러에 의해 자동으로 호출되며, 사용이 끝난 객체를 정리해 줍니다.
C++에서 클래스 소멸자의 이름은 해당 클래스의 이름과 같으며, 이름 앞에 물결 표시(tilde, ~)를 붙여 생성자와 구분합니다. 즉, 예를 들어 Book 클래스의 소멸자는 ~Book()이라는 이름을 가지게 됩니다.
이러한 소멸자는 다음과 같은 특징을 가집니다.
1. 소멸자는 인수를 가지지 않습니다.
2. 소멸자는 반환값이 없지만 void형으로 선언하지 않습니다.
3. 객체는 여러 개의 생성자를 가질 수 있지만, 소멸자는 단 하나만 가질 수 있습니다.
소멸자의 호출
C++에서 소멸자의 호출 시기는 컴파일러가 알아서 처리하게 됩니다.
C++에서 객체가 선언된 메모리 영역별로 소멸자가 호출되는 시기는 다음과 같습니다.
메모리 영역 소멸자 호출 시기
데이터(data) 영역 | 해당 프로그램이 종료될 때 |
스택(stack) 영역 | 해당 객체가 정의된 블록을 벗어날 때 |
힙(heap) 영역 | delete를 사용하여 해당 객체의 메모리를 반환할 때 |
임시 객체 | 임시 객체의 사용을 마쳤을 때 |
<참고>
http://www.tcpschool.com/cpp/cpp_conDestructor_constructor
열혈 c++ 프로그래밍
'C++' 카테고리의 다른 글
(C++) 범위 기반 for문과 auto 키워드 (0) | 2022.11.07 |
---|---|
(C++) Visual Studio #include <bits/stdc++.h> (0) | 2022.10.07 |
(C++) 클래스의 상속 (부모 클래스, 자식 클래스) (0) | 2022.09.03 |
(C++) 정적 멤버와 상수 멤버(static, const) (0) | 2022.09.03 |
(C++) 메모리의 동적 할당 (0) | 2022.08.20 |