PART 2. 객체 지향 프로그래밍
- chapter 6. 생성자와 소멸자
1. 생성자(Constructor)
- 생성자는 객체가 생성될 때 자동으로 호출되어 객체를 초기화하는 역할을 한다.
- 생성자는 클래스와 이름이 같고, 반환값이 없는 함수이다. 매개변수는 받으니 여러개의 생성자를 중복 정의할 수 있다.
- 생성자가 호출되지 않으면 객체 생성도 안되므로 특수한 경우를 제외하고 생성자는 public으로 선언되어 있어야 한다.
- 생성자를 명시적으로 작성하지 않았다면 자동으로 디폴트 생성자가 추가되며, 객체가 생성될 때 디폴트 생성자가 호출된다.
- 자동으로 추가된 디폴트 생성자는 매개변수도 없고, 함수 본문도 없으므로 아무일도 하지 않는다.
- 다른 생성자를 추가하면 디폴트 생성자는 자동으로 추가되지 않으므로 두가지를 같이 사용하려면 디폴트 생성자도 명시적으로 정의해둬야 한다.
- 매개변수를 갖지 않는 디폴트 생성자 안에 특정한 작업을 추가해두면 객체가 생성될 때마다 그 작업이 수행된다.
- 매개변수를 갖는 생성자와 갖지 않는 생성자를 오버로딩해서 작성해두면 객체를 생성할 때 인자전달여부에 따라서 다른 생성자가 호출된다.
- 생성자를 호출하는 방법
1) clock a; // 매개변수가 없는 생성자 호출
2) clock b("b",12); // 매개변수가 있는 생성자 호출
3) clock c = clock("c",24); // 임시객체를 만들고 이것을 c에 복사
- 생성자를 호출하는 방법이 아닌 것
1) clock f(); // 클래스를 반환하는 f()라는 함수의 원형 선언.
2) clock g = clock; //컴파일 에러; E0254 : 형식 이름을 사용할 수 없습니다.
- 생성자에도 디폴트 매개 변수를 줄 수 있다.
- 생성자안에서 다른 생성자를 호출 할 수 있다. (가독성 문제로 거의 사용하지 않는다.)
2. 소멸자(Destructor)
- 소멸자는 객체가 소멸될 때 자동으로 호출되어 마무리 작업을 해주는 역할을 한다.
- 소멸자는 클래스 이름 앞에 ~가 붙으며, 반환값이 없다. 매개변수도 받지 않으므로 소멸자는 중복 정의할 수 없다.
- 소멸자를 호출할 수 없는 상황에서도 객체 생성이 안되므로 특수한 경우를 제외하고 소멸자도 public으로 선언되어 있어야 한다.
- 소멸자를 명시적으로 작성하지 않았다면 자동으로 디폴트 소멸자가 추가되며, 객체가 소멸될 때 디폴트 소멸자가 호출된다.
- 자동으로 추가된 디폴트 소멸자는 함수 본문이 없으므로 아무런 일도 하지 않는다.
- 객체 안에서 동적 할당 등을 한 경우 주로 소멸자에서 이를 처리한다.
(객체 생성시 생성자와 소멸자를 호출할 수 없을 때 나오는 에러 : C2248 'clock::~clock': private 멤버('clock' 클래스에서 선언)에 액세스할 수 없습니다.)
3. 멤버 초기화 목록(member initialization list)
- 생성자 함수 헤더와 몸체 사이에 : (콜론)을 찍고 "멤버(매개변수)"의 형식으로 멤버의 초기값을 나열하는 것.
- 문법
clock(int h, int m, int s) : hour(h), min(m), sec(s) { }
- 가독성이 그리 좋진 않아서 잘 쓰진 않지만 꼭 필요한 경우가 몇가지 있다.
1) 멤버가 상수일 때 (다만 C++11에서는 in-class initilizer로 초기화가 가능하다.)
2) 멤버가 참조자일 때
3) 멤버가 디폴트 생성자를 사용하지 않는 객체일 때
4. 복사 생성자
- 한 객체의 내용을 복사한 새로운 객체를 만들때 호출된다.
clock c = clock("third", 12); clock d = c; // 이 때 복사 생성자가 호출된다. d = c; // 이 때는 복사 생성자가 호출되지 않는다.
-문법
clock(const clock& obj); clock::clock(const clock& obj) : clockName(obj.clockName), MAX_HOUR(obj.MAX_HOUR) { cout << ClockCount << "복사 생성자 호출" << endl; ClockCount++; hour = obj.hour; min = obj.min; sec = obj.sec; }
- const가 붙으므로 원본 데이터를 수정할 수 없다.
clock::clock(const clock& obj) : clockName(obj.clockName), MAX_HOUR(obj.MAX_HOUR) { cout << ClockCount << "복사 생성자 호출" << endl; ClockCount++; hour = obj.hour++; obj.min = 10; min = obj.min; obj.sec *= 5; sec = obj.sec; } //이런거 안됨.
- 복사 생성자도 명시적으로 선언하지 않으면 디폴트 복사 생성자가 자동으로 추가된다.
- 복사 생성자는 매개변수 형태가 정해져 있으므로 중복 정의 할 수 없다.
- 얕은 복사와 깊은 복사의 개념은 생략
- 클래스 안에 포인터가 있을 경우 디폴트 복사 생성자로는 얕은 복사가 되므로 복사 생성자를 정의해서 깊은 복사가 되도록 해줘야 한다.
- 얕은 복사가 된 상태에서 소멸자에서 동적할당을 해제하는 경우 같은 곳을 두번 해제하게 되므로 런타임 에러가 발생한다.
- 복사 생성자가 호출되는 경우
1) 객체를 복사 생성하는 경우
2) 객체를 함수의 매개변수로 전달하는 경우
3) 함수가 객체를 반환하는 경우
+ 디폴트 멤버 함수
1) 디폴트 생성자
2) 디폴트 소멸자
3) 디폴트 복사 생성자
4) 디폴트 대입 연산자
clock.h
#pragma once #include <iostream> #include <string> using std::cout; using std::cin; using std::endl; using std::string; class aliasName { string& alias; public: string getName(); aliasName(string name); ~aliasName(); }; class clock { private: //static int ClockCount = 0; //in-class initilizer가 있는 멤버는 const여야 합니다. static int ClockCount; //C++11에서부터 in-class initilizer가 가능해졌다. (standard에서는 안 됨) // const int MAX_HOUR = 24; //12 or 24 //C++11 const int MAX_HOUR; //standard int hour; int min; int sec; aliasName clockName; public: void setClock(int h, int m, int s); void showClock(); clock(string name, int MAX); clock(string name); clock(const clock& obj); ~clock(); };
clock.cpp
#include "clock.h" int clock::ClockCount = 0; void clock::setClock(int h, int m, int s) { hour = h /MAX_HOUR + m / 60; min = m % 60 + s / 60; sec = s % 60; } void clock::showClock() { cout << ClockCount << "번째 시계 : " << clockName.getName() << endl; cout << hour << "시 " << min << "분 " << sec << "초 " << endl; } clock::clock(string name, int MAX) : clockName(name), MAX_HOUR(MAX) { cout << ClockCount << "멤버 초기화 목록을 사용하는 생성자 호출. (멤버 변수가 상수와 객체)" << endl; ClockCount++; hour = 0; min = 0; sec = 0; } clock::clock(string name) : clockName(name), MAX_HOUR(24) { cout << ClockCount << "멤버 초기화 목록을 사용하는 생성자 호출. (멤버 변수가 객체)" << endl; ClockCount++; hour = 0; min = 0; sec = 0; } clock::clock(const clock& obj) : clockName(obj.clockName), MAX_HOUR(obj.MAX_HOUR) { cout << ClockCount << "복사 생성자 호출" << endl; ClockCount++; hour = obj.hour; min = obj.min; sec = obj.sec; } clock::~clock() { cout << ClockCount << "디폴트 소멸자 호출" << endl; ClockCount--; } string aliasName::getName() { return alias; } aliasName::aliasName(string name) : alias(name) { cout << "객체 속의 객체 생성" << endl; } aliasName::~aliasName() { }
CaD.cpp
#include "clock.h" int main() { clock a("first"); clock b("second", 12); clock c = clock("third", 12); clock d = c; }