연재 완료/C++ Lang 이론

6. 생성자와 소멸자

라이피 (Lypi) 2018. 6. 26. 14:42
반응형

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;
}



반응형