재수강해야하는  과목 수강신청에서 밀려났읍니다...

내 학점...ㅠㅠ

여튼 그래서 C++ 업데이트 한동안 보류

'일기장' 카테고리의 다른 글

수강신청 망했따...  (0) 2019.09.09
종강...!  (0) 2019.08.08

*본 시리즈의 내용은 Data Structures and Algorithms in C++, Second Edition (GOODRICH, M., TAMASSIA, R. 및 MOUNT, D. 저, 2011) 에서 가져옴을 알립니다*

생성자 (constructor)

생성자는 클래스의 멤버 데이터를 초기화하는 기능을 하는 특수한 멤버함수이다. 새로운 클래스 객체의 존재가 확인되면 호출된다. 

생성자는 클래스의 이름과 동일해야하며, 반환형이 존재하 않는다. 주로 객체의 자료형에 따라 멤버 데이터가 초기화되는 방법이 한가지 이상 존재할 수 있기 때문에, 오버로딩을 위해 여러개를 작성한다.

구조체편에서 다루었던 Passenger 구조체를 사용해 예를 들어본다. 다음은 Passenger 구조체를 클래스화 시킨것이다:

class Passenger
{
	public:
    	Passenger();	//기본 생성자
        Passenger(const string& nm, MealTyper mp, const string& ffn = "NONE");
        //복제 생성자
        Passenger(const Passenger& pass);	
        
        bool isFrequentFlyer() const;
        
        void makeFrequentFlyer(Const string& newFreqFlyerNo);
        // ...
    
    private:
    	string name;
        MealType mealPref;
        bool isFreqFlyer;
        string freqFlyerNo;
};

첫번째 생성자처럼 인수를 받지 않는 생성자를 기본 생성자(default constructor)라고 한다. 

그에 반해 두번째 생성자는 인수를 받아 객체의 멤버데이터를 초기화 시킨다. 매개변수 안의 = "NONE"은, 인수를 생략해도 된다는 뜻이며, 생략할 시 "NONE" 값을 대신 인수로 받는다는 뜻이다. 만약 새 passenger객체의 isFreqFlyer값이 false라면 이 인수르 받지 않는다는 뜻이다. 

세번째 생성자는 Passenger 클래스의 참조를 받아 그 내용을 복사하며 이를 복제 생성자(copy constructor)라고 한다.

아래는 위 클래스의 구현이다:

Passenger::Passenger()
{
	name = "--NO NAME--";
    mealPref = NO_PREF;
    isFreqFlyer = false;
    freqFlyerNo = "NONE";
}

Passenger::Passenger(const string% nm; MealType mp; const string& ffn)
{
	name = nm;
    mealPref = mp;
    isFreqFlyer = (ffn != "NONE");		// ffn을 인수로 받을때만 true 반환;
    freqFlyerNo = ffn;
}

Passenger::Passenger(const Passenger& pass)
{
	name = pass.name;
    mealPref = pass.mealPref;
    isFreqFlyer = pass.isFreqFlyer;
    freqFlyerNo = pass.freqFlyerNo;
}

여기서 한가지 주의해야 할 점은, 두번째 생성자가 string을 인수로 받고 있다는 점이다. string 또한 하나의 클래스이며 자체적으로 재정의한 연산자가 있기때문에 "name = nm"을 사용해도 문제가 없다. 그러나 그렇지 않을 경우를 대비해 C++에서는 초기화 리스트 (initializing lists)를 제공하며 이는 함수 선언 이후에 :멤버_이름(초기_값)의 형태를 추가하여 사용한다. 이를 이용해 두번째 생성자를 재작성한다: 

Passenger::Passenger(const string& nm, MealType mp, string ggn)
: name(nm), mealPref(mp), isFreqFlyer(ffn != "NONE")
{
	함수정의;
}

소멸자(destructor)

소멸자는 해당 객체가 소멸될때 자동으로 호출되는 멤버함수로, 만약 객체가 동적으로 메모리를 할당받았다면 해당 메모리를 해제하며, 객체 내 지역변수를 소멸시킨다. 

소멸자는 생성자 앞에 '~' 를 추가하여 작성하며 반환형은 없다. 다음은 소멸자로 클래스의 메모리 동적할당을 해제하는 예이다:

class Vect
{
	public:
    	Vect(int n);	// 생성자;
        ~Vect();		// 소멸자;
        
        // 다른 공용멤버;
    private:
    	int* data;
        int size;
};

Vect::Vect(int n)
{
	size = n;
    data = new int[n];
}

Vect::~Vect()
{
	delete [] data;
}

C++에서 소멸자가 꼭 필요한것은 아니나, 만약 클래스 내부에서 동적으로 메모리를 할당했다면 메모리 누수를 막기 위해 이를 해제해야 하기 때문에 소멸자는 필수이다. 

*본 시리즈의 내용은 Data Structures and Algorithms in C++, Second Edition (GOODRICH, M., TAMASSIA, R. 및 MOUNT, D. 저, 2011) 에서 가져옴을 알립니다*

마침내 C++에서 가장 핵심적이고 객체지향이라는 프로그래밍 패러다임의 중추를 맡고 있는 클래스를 다룰 시간이다. 

클래스는 새로운 자료형들을 정의하고, 관련된 함수와 연산자들을 작성하게 해준다. 또한, 의도적으로 특정 멤버에 접근하지 못하게 함으로 클래스가 올바르게 작동하기 위해 필수적인 요소들을 구현과정에서 분리한다. 


클래스 구조체 (Class Structure)

클래스 는 멤버(member) 들로 이루어저 있다.

변수/상수들은 자료 멤버(data member) 혹은 멤버 변수(member variables), 함수들은 멤버 함수(member function) 혹은 메소드/메서드 (methods) 라고 불린다. 직관성과 통일성을 위해 이 이후로 명칭을 멤버 변수/함수로 통일한다. 

멤버 변수들은 어떠한 기본 자료형, 포인터, 클래스 참조자 혹은 클래스 자기 자신이 될 수 있다.  멤버 함수들은 주로 멤버 변수들을 다루며, 클래스의 주 목적과 행동을 정의한다. 

간단한 예로 Counter 하는 클래스를 선언해보자. 선언 자체는 주로 별개의 .h 파일 (헤더 파일) 에서 이루어지며 멤버 함수또한 선언만 한다. Counter 클래스 내부에는 counter 라는 수를 세는 멤버 변수가 있으며 counter의 값을 초기화하는 생성자 함수, counter의 현재 값을 반환하는 getCount()함수, 그리고 counter의 값을 증가시키는 increaseBy() 함수가 있다:

// customClass.h;
#include <iostream>

class Counter
{
	public:
    	Counter();				// count의 값을 초기화하는 생성자 함수;
        int getCount();			// count의 값을 반환하는 멤버 함수;
        void increaseBy(int x);	// count의 값을 x만큼 증가시키는 멤버 함수;
        
	private:
    	int count;				// 멤버 변수 카운터;
}

클래스 내부가 privatepublic 두 구역으로 나누어지는걸 볼 수 있다.


public 구역은 클래스 내부의 공용 인터페이스(public interface)를 정의한다. 이 구역내의 존재하는 모든 멤버 변수와 함수들은 클래스를 사용하는 유저들이 직접 접근할 수 있는 존재들이다. 

반면, private 구역이 정의하는 전용 인터페이스(private interface)에는 일반 유저가 접근 할 수 없는 자료들을 포함한다.  이러한 접근의 구분을 캡슐화(encapsulation)이라고 부른다.

간단하게 정의하자면, 공용 으로 선언된 멤버들은 클래스 밖에서도 집접 접근할 수 있지만, 전용으로 선언된 멤버들은 클래스 내부에서만 접근 할 수 있다. 

그런데 왜 굳이 번거롭게 멤버를 전용으로 선언할까? 이를 설명하기 위해서는 C++의 프로그래밍 패러다임인 객체지향을 이해해야 한다 (후에 링크 추가 예정). 그 전까지는, 유저에게 구현에 문제없이 클래스를 사용할 수 있는 가장 깔끔한 형태의 인터페이스를 제공하기 위함이라고 함축한다. 

C++ 매뉴얼서는 공용인터페이스가 유저와 더 가깝기 떄문에 공용 인터페이스를 먼저 선언하라고 권장한다. 하지만 전용 인터페이스를 강조하기 위해 지켜지지 않을 때도 있다.


다시 클래스로 돌아와, 클래스를 선언했으니 이제 클래스 내부 멤버 함수들을 정의할 차례이다. 멤버 함수들은 보통 클래스 내부에서 정의하지 않으며, 별개의 .cpp 파일에서 정의한다. 컴파일러에게 Counter 클래스 내부의 함수들을 정의한다는것을 분명하게 하기 위해, 각 함수 앞에 범위 'Counter::' 를 추가한다:

// customClass.cpp;
#include "customClass.h"

Counter::Counter() {count = 0;}			// count 변수 초기화;
int Counter::getCount()	{return count;}		// count 값 반환;
void Counter::increseBy(int x) {count += x;}	// count 값 x 만큼 증가;

 

첫번째 멤버 함수는 클래스와 같은 이름을 가지고 있다. 이러한 멤버함수들은 생성자(constructor) 라고 부른다. 생성자는 클래스의 멤버 변수를 초기화하는 역할을 맡고 있다. 

getCount() 함수는 주로 "getter" 라고 불리는 함수의 역할을 한다. 이러한 함수들은 간접적으로 전용 인터페이스 내부의 멤버 변수에 접근할 수 있게 해준다. (직접적으로 수정은 불가하고 현재의 값만 볼 수 있다). 

다음은 선언하고 구현한 Counter 클래스를 메인 파일에서 사용하는 예이다:

// main.cpp;

#include <iostream>
#include "customClass.h"

int main()
{
    Counter ctr;			        // ctr이란 이름을 가진 Counter 객체 생성및 초기화;
    cout << ctr.getCount() << endl;	// ctr객체 내부의 getter 함수 실행 및 출력;
    ctr.increseBy(3);			    // 해당 함수 실행;
    cout << ctr.getCount() << endl; 
    ctr.increaseBy(5);
    cout << ctr.getCount() << endl;
    
    return 0;
}

결과)

0
3
8

 

+ Recent posts