구조체가 주는 이점은 연관있는 데이터를 하나로 묶으면, 프로그램의 구현 및 관리가 용이하다는 점입니다. 소프트웨어를 단순하게 표현하면 데이터의 표현과 데이터의 처리입니다. 표현해야 하는 데이터는 항상 부류를 형성하기 마련입니다. 이렇게 부류를 형성하는 대이터들은 함께 생성, 이동 및 소멸된다는 특성이 있습니다. 구조체는 연관 있는 데이터를 묶을 수 있는 문법적 자잋로 데이터의 표현에 매우 큰 도움을 줍니다. struct car basicCar; 삽입된 키워드 struct는 이어서 선언되는 자료형이 구조체를 기반으로 정의된 자료형임을 나타냅니다. 키워드 struct를 생략하려면 별도의 typedef 선언을 추가해야 합니다. C++ 에서는 별도의 typedef 선언 없이도 다음과 같이 변수를 선언할 수 있습니다.
#include <iostream> using namespace std; #define ID_LEN 20 #define MAX_SPD 200 #define FUEL_STEP 2 #define ACC_STEP 10 #define BRK_STEP 10 struct Car { char gamerID[ID_LEN]; int fuelGauge; int curSpeed; }; void ShowCarState(const Car &car) { cout << "ID: " << car.gamerID << endl; cout << "Fule: " << car.fuelGauge << "%" << endl; cout << "Speed: " << car.curSpeed << "km/s" << endl; } void Accel(Car &car) { if (car.fuelGauge <= 0) return; else car.fuelGauge -= FUEL_STEP; if (car.curSpeed + ACC_STEP >= MAX_SPD) { car.curSpeed = MAX_SPD; return; } car.curSpeed += ACC_STEP; } void Break(Car &car) { if (car.curSpeed < BRK_STEP) { car.curSpeed = 0; return; } car.curSpeed -= BRK_STEP; } int main(void) { Car run99 = { "run99", 100, 0 }; Accel(run99); Accel(run99); ShowCarState(run99); Break(run99); ShowCarState(run99); Car sped77 = { "sped77", 100, 0 }; Accel(sped77); Break(sped77); ShowCarState(sped77); return 0; } |
구조체 Car 와 관련된 정보를 상수화하였습니다. 각각의 상수가 의미하는 바는 관련 함수를 통해서 이해할 수 있습니다. 차의 정보를 출력하는 기능의 함수입니다. 단순히 정보를 출력만하기 때문에 const 참조자를 매개변수로 선언하였습니다. 차의 가속을 위해서 엑셀을 밝은 상황을 표현해 놓은 함수입니다. 엑셀을 밟을 때마다 연료가 줄어들고 스피드가 올라가는 상황을 단순히 표현하였습니다. 브레이크를 밟은 상황을 표현한 함수 입니다. 엑셀을 밟을 때마다 연료가 줄어들고 스피드가 올라가는 상황을 단순히 표현하였습니다. 구조체 변수의 선언 및 초기화가 진행되었습니다. 엑셀과 브레이크를 밟은 상황을 연출하고 있습니다. 구조체 Car와 함께 부류를 형성하여, Car와 관련된 데이터의 처리를 담당하는 함수들입니다. 따라서 위의 함수들은 구조체 Car에 종속적인 함수들이라고 말할 수 있습니다. 그럼에도 불구하고 전역함수의 형태를 띠기 대문에, 이 함수들이 구조체 Car에 종속적임을 나타내지 못하고 있는 상황입니다.
구조체 안에 함수 삽입하기
구조체 Car에 종속적인 함수들을 구조체 안에 함께 묶어 버리면 어떨까요 자동차와 관련된 데이터와 함수를 모두 묶는 셈이 되기 때문에 보다 확실한 구분이 가능합니다. C++에서는 구조체 안에 함수를 삽입하는 것을 허용합니다.
#include <iostream> using namespace std; #define ID_LEN 20 #define MAX_SPD 200 #define FUEL_STEP 2 #define ACC_STEP 10 #define BRK_STEP 10 struct Car { char gamerID[ID_LEN]; int fuelGauge; int curSpeed; void ShowCarState() { cout << "ID: " << gamerID << endl; cout << "fuel: " << fuelGauge << "%" << endl; cout << "Speed: " << curSpeed << "km/s" << endl << endl; } void Accel() { if (fuelGauge <= 0) return; else fuelGauge -= FUEL_STEP; if (curSpeed + ACC_STEP >= MAX_SPD) { curSpeed = MAX_SPD; return; } curSpeed += ACC_STEP; } void Break() { if (curSpeed < BRK_STEP) { curSpeed = 0; return; } curSpeed -= BRK_STEP; } }; int main(void) { Car run99 = { "run99", 100, 0 }; run99.Accel(); run99.Accel(); run99.ShowCarState(); run99.Break(); run99.ShowCarState(); Car sped77 = { "sped77", 100, 0 }; sped77.Accel(); sped77.Break(); sped77.ShowCarState(); return 0; } |
함수를 멤버로 지니는 구조체의 정의를 보이고 있습니다. 초기화의 대상은 함수가 아닌 변수입니다. 따라서 함수가 삽입되었어도 초기화의 방법은 달라지지 않습니다. 구조체 run99에 존재하는, Accel 함수를 호출하고 있습니다. 구조체 내에 선언된 변수에 접근하는 방법과 동일한 방식으로 함수의 호출이 이뤄집니다. 또 다른 구조체 변수를 선언하였습니다. 그리고 이번에는 이 구조체를 대상으로 Accel 함수를 호출하고 있습니다. 이렇듯 구조체 내에 함수가 정의되었기 때문에, 구조체 변수를 대상으로 함수의 호출이 이뤄져야 합니다.
구조체 안에 enum 상수의 선언
열거형 enum 을 이용해서 구조체 내에서만 유효한 상수를 정의하면 딥니다. enum의 선언을 구조체 내부에 삽입하는 것이 부담스럽다면, 이름공간을 이용해서 상수가 사용되는 영역을 명시하는 것 또 다른 방법이 될 수 있습니다. 이렇게 이름공간을 이용하면, 몇몇 구조체들 사이에서만 사용하는 상수들을 선언할 때 특히 도움이 되며, 가독성도 좋아지는 경향이 있습니다. CAR_CONST 이름 공간 안에 구조체 Car에서 사용하는 상수들을 모두 모아 놓았습니다. 상수 ID_LEN의 접근을 위해서 이름공간 CAR_CONST를 지정하고 있습니다. 이렇듯 이름공간을 지정해서 코드를 작성했기 떄문에, 이문장만 봐도 이 상수가 어느 영역에서 선언되고 사용되는 상수인지 쉽게 알 수 있습니다.
함수는 외부로 뺄 수 있습니다.
함수가 포함되어 있는 C++의 구조체를 보는 순간, 다음의 정보들이 쉽게 눈에 들어와야 코드의 분석이 용이합니다. 선언되어 있는 변수 정보와 정의되어 있는 함수 정보입니다. 보통 프로그램을 분석할 때, 흐름 및 골격 위주로 분석하는 경우가 많습니다. 그리고 이러한 경우 함수기능만 파악을 하지, 함수의 세부 구현까지 신경을 쓰지 않습니다. 따라서 구조체를 보는 순간, 정의되어 있는 함수의 종류와 기능이 한눈에 들어오게끔 코드를 작성하는 것이 좋습니다. 따라서 구조체 내에 정의된 함수의 수가 많거나 그 길이가 길다면, 구조체 밖으로 함수를 빼낼 필요가 있습니다. 즉, 함수의 원형선언을 구조체 안에 두고, 함수의 정의를 구조체 밖으로 빼내는 것입니다. 빼낸 다음에 해당 함수가 어디에 정의되어 있는지에 대한 정보만 추가해주면 됩니다. 구조체 안에 함수의 원형만 남으니, 함수의 종류가 한눈에 들어오고, 적절한 주석을 통해서 함수의 기능도 쉽게 판달 할 수 있습니다. 원래 속하는 구조체의 이름을 명시하면서 구조체 밖으로 함수의 정의가 빠져 나왔습니다.
#include <iostream> using namespace std; namespace CAR_CONST { enum { ID_LEN = 20, MAX_SPD = 200, FUEL_STEP = 2, ACC_STEP = 10, BRK_STEP = 10 }; } struct Car { char gamerID[CAR_CONST::ID_LEN]; int fuelGauge; int curSpeed; void ShowCarState(); void Accel(); void Break(); }; void Car::ShowCarState() { cout << "ID: " << gamerID << endl; cout << "fuel: " << fuelGauge << "%" << endl; cout << "Speed: " << curSpeed << "km/s" << endl << endl; } void Car::Accel() { if (fuelGauge <= 0) return; else fuelGauge -= CAR_CONST::FUEL_STEP; if ((curSpeed + CAR_CONST::ACC_STEP) >= CAR_CONST::MAX_SPD) { curSpeed = CAR_CONST::MAX_SPD; return; } curSpeed += CAR_CONST::ACC_STEP; } void Car::Break() { if (curSpeed < CAR_CONST::BRK_STEP) { curSpeed = 0; return; } curSpeed -= CAR_CONST::BRK_STEP; } int main(void) { Car run99 = { "run99", 100, 0 }; run99.Accel(); run99.ShowCarState(); run99.Break(); run99.ShowCarState(); return 0; } |
구조체 안에 함수의 원형만 남으니 , 함수의 종류가 한눈에 들어오고, 적절한 주석을 통해서 함수의 기능도 쉽게 판단이 됩니다. 원래 속하는 구조체의 이름을 명시하면서 구조체 밖으로 함수의 정의가 빠져 나왔습니다. 구조체 안에 함수가 정의되어 있으면, 함수를 인라인으로 처리가 됩니다. 반면, 위의 예제와 같이 함수를 구조체 밖으로 빼내면, 이러한 의미가 사라집니다. 따라서 인라인의 의미를 그대로 유지하려면 다음과 같이 키워드 inline을 이용해서 인라인 처리를 명시적으로 지시해야 합니다. inline void Car::ShowCarState() {} C++에서의 구조체는 클래스의 일종으로 간주됩니다. 그래서 구조체 안에 함수를 정의할 수 있었던 것입니다. 위에서 정의한 구조체를 가리켜 그냥 클래스라고 표현해도 틀리지 않습니다.
클래스와 객체
클래스와 구조체의 유일한 차이점.
키워드 struct 를 대신하여 class 를 사용하면, 구조체가 아닌 클래스가 됩니다. 그런데 이렇게 키워드를 바꿔놓으면 앞의 예제에서 보였던 다음의 방식으로 변수(구조체 변수)를 선언하지 못합니다. Car run99 = {"run99", 100, 0}; 이유는 클래스 내에 선언된 함수가 아닌, 다른 영역에서 변수를 초기화하려 했기 떄문입니다. 클래스는 기본적으로 클래스 내에 선언된 변수는 클래스 내에 선언된 함수에서만 접근 가능합니다. Car run99; 접근과 관련하여 별도의 선언을 하지 않으면, 클래스 내에 선언된 변수 및 함수에 대한 접근은 허용하지 않을 테니, 접근과 관련된 지시를 별도로 내려주세요. 이렇듯 클래스는 정의를 하는 과정에서 각각의 변수 및 함수의 접근 허용범위를 별도로 선언해야 합니다. 이것이 키워드 struct를 이용해서 정의하는 구조체와 키워드 class를 이용해서 정의하는 클래스의 유일한 차이점입니다.
접근 제어 지시자(접근 제어 레이블)
C++ 에서 접근 제어 지시자는 다음과 같이 총 세가지로 존재합니다. public, protected, private. 그리고 이들 각각이 의미하는 바는 다음과 같습니다. public 은 어디서든 접근허용을 뜻합니다. protected 은 상속관계에 놓여있을 때, 유도 클래스에서의 접근허용, private 은 클래스 내에서만 접근을 허용합니다.
include <iostream> #include <cstring> using namespace std; namespace CAR_CONST { enum { ID_LEN=20, MAX_SPD=200, FUEL_STEP=2, ACC_STEP=10, BRK_STEP=10 }; } class Car { private: char gamerID[CAR_CONST::ID_LEN]; int fuelGauge; int curSpeed; public: void IniMembers(char * ID, int fuel); void ShowCarState(); void Accel(); void Break(); }; void Car::IniMembers(char * ID, int fuel) { strcpy(gamerID, ID); fuelGauge = fuel; curSpeed = 0; } void Car::ShowCarState() { cout << "Id: " << gamerID << endl; cout << "fuel: " << fuelGauge << "%" << endl; cout << "Speed: " << curSpeed << "km/s" << endl << endl; } void Car::Accel() { if (fuelGauge <= 0) return; else fuelGauge -= CAR_CONST::FUEL_STEP; if ((curSpeed + CAR_CONST::ACC_STEP) >= CAR_CONST::MAX_SPD) { curSpeed = CAR_CONST::MAX_SPD; return; } curSpeed += CAR_CONST::ACC_STEP; } void Car::Break() { if (curSpeed < CAR_CONST::BRK_STEP) { curSpeed = 0; return; } curSpeed -= CAR_CONST::BRK_STEP; } int main(void) { Car run99; run99.IniMembers("run", 100); run99.Accel(); run99.Accel(); run99.Accel(); run99.ShowCarState(); run99.Break(); run99.ShowCarState(); return 0; } |
struct 대신해서 class 선언이 삽입되었습니다. 이는 클래스의 정의에 해당합니다. 접근제어 지시자 중 하나인 private 선언되었으므로, 이어서 등장하는 변수와 함수는 private에 해당하는 범위 내에서 (클래스 내에서만)접근이 가능합니다. 접근제어 지시자중 하나인 public이 선언되었으므로, 이어서 등장하는 변수와 함수는 public 에 해당하는 범위 내에서(어디서든) 접근이 가능합니다. 클래스 안에 선언된 변수의 초기화를 목적으로 정의된 함수입니다. 변수가 모두 private으로 선언되어서 main 함수에서 접근이 불가능합니다. 하지만 이 함수는 동일 클래스 내에서 정의된 함수이므로 접근이 가능합니다. 뿐만 아니라, 이 함수는 public 으로 선언되어서 main 함수에서 호출이 가능합니다. 따랏 main 함수에서는 이 함수의 호출을 통해서 클래스 안에 선언된 변수를 초기화할 수 있습니다. 초기화를 목적으로 함수를 호출하고 있습니다. 이 함수는 ID 정보와 연료량 정보를 전달받아서 초기화되는 형태로 정의되었습니다. 단, 변수 curSpeed는 무조건 0으로 초기화 되도록 정의되었습니다. 함수호출이 가능한 이유는 함수가 모두 public 으로 선언되었기 때문입니다.
접근제어 지시자 A가 선언되면, 그이후에 등장하는 변수나 함수는 A에 해당하는 범위 내에서 접근이 가능합니다. 새로운 접근 제어 지시자 B가 선언되면, 그 이후로 등장하는 변수나 함수는 B에해당하는 범위 내에서 접근이 가능합니다. 함수의 정의를 클래스 밖으로 빼도, 이는 클래스의 일부이기 때문에, 함수 내에서는 private으로 선언된 변수에 접근이 가능합니다. 키워드 struct를 이용해서 정의한 구조체에 선언된 변수와 함수에 별도의 접근제어 지시자를 선언하지 않으면, 모든 변수와 함수는 public으로 선언됩니다. 키워드 class를 이용해서 정의한 클래스에 선언된 변수와 함수에 별도의 접근제어 지시자를 선언하지 않으면, 모든 변수와 함수는 private 으로 선언됩니다.
접근제어 지시자의 뒤에는 세미콜론이 아닌 콜론이 붙습니다. 이는 접근제어 지시자가 특정 위치정보를 알리는 레이블(라벨)이기 때문입니다. 우리가 알고 있는, switch문에 사용되는 case도 레이블이기 때문에 콜론이 붙습니다.
구조체와 클래스는 변수의 성격만 지니는 것이 아니니다. 그래서 변수라는 표현을 대신해서 객체라는 표현을 사용합니다. 객체는 무엇을 의미하나요 그리고 객체라고 부른느 이유는 무엇인가요. run99는 변수가 아닌 객체입니다. 그리고 클래스를 구성하는(클래스 내에 선언된) 변수를 가리켜 멤버변수라 하고, 클래스를 구성하는(클래스 내에 정의된) 함수를 가리켜 멤버함수라 합니다. 멤버는 구성원이라는 의미를 담고 있습니다. 그래서 클래스를 구성하는 변수와 함수를 각각 멤버변수, 멤버함수라는 이름을 붙인 것입니다.
C++에서의 파일분할
어떠한 프로그램이건 하나의 파일에 모든 것을 담지 않습니다. 특히 C++은 클래스 별로 헤더ㅏ일과 소스파일을 생성해서 클래스의 선언과 정의를 분리하는 경우가 많기 때문에 많은 수의 파일이 만들어집니다. 그럼 이어서 클래스를 대상으로, 파일을 나누는 기준을 설명하겠습니다. C언어를 공부하는 과정에서 익힌 내용들입니다. 헤더파일의 역할을 알고 있습니다. C언어를 대상으로 헤더파일에 들어가야 할 내용을 구분할 수 있습니다. 헤더파일의 중복포함을 막기 위해서 사용하는 매크로 #ifndef~#endif를 알고 있습니다. 둘 이상의 파일을 컴파일해서 하느이 실행파일을 만드는 법을 알고 있습니다. 링커가 하는 일을 알고 있습니다. 컴파일러가 Car 클래스와 관련된 문장의 오류를 잡아내는데 필요한 최소한의 정보로써, 클래스를 구성하는 외형적인 틀을 보여줍니다. 따라서 이를 가리켜 클래스의 선언이라 합니다. 즉, 위의 정보는 클래스 Car와 관련된 문장의 옳고 그름을 판단하는데 사용됩니다. 반면, 클래스의 정의에 해당하는 다음 함수의 정의는 다른 문장의 컴파일에 필요한 정보를 가지고 있지 않습니다. 따라서 함수의 정의는 컴파일 된 이후에, 링커에 의해 하나의 실행파일로 묶이기만 하면 됩니다.
Car 클래스와 관련된 문장의 컴파일 정보로 사용되는 클래스의 선언은 헤더파일에 저장을 해서, 필요한 위치에 쉽게 포함될 수 있도록 해야 하며, 크래스의 정의는 소스파일에 저장해서, 컴파일이 되도록 하면 됩니다.
#ifndef __CAR_H__ #define __CAR_H__ namespace CAR_CONST { enum { ID_LEN=20, MAX_SPD=200, FUEL_STEP=2, ACC_STEP=10, BRK_STEP=10 }; } class Car { private: char gamerID[CAR_CONST::ID_LEN]; int fuelGauge; int curSpeed; public: void InitMembers(char *ID, int fuel); void ShowCarState(); void Accel(); void Break(); }; #endif |
#pragma warning(disable:4996) #include <iostream> #include <cstring> #include "Car.h" using namespace std; void Car::InitMembers(char * ID, int fuel) { strcpy(gamerID, ID); fuelGauge = fuel; curSpeed = 0; } void Car::ShowCarState() { cout << "ID: " << gamerID << endl; cout << "fuel: " << fuelGauge << "%" << endl; cout << "Speed: " << curSpeed << "km/s" << endl; } void Car::Accel() { if (fuelGauge <= 0) return; else fuelGauge -= CAR_CONST::FUEL_STEP; if ((curSpeed + CAR_CONST::ACC_STEP) >= CAR_CONST::MAX_SPD) { curSpeed = CAR_CONST::MAX_SPD; return; } curSpeed += CAR_CONST::ACC_STEP; } void Car::Break() { if (curSpeed < CAR_CONST::BRK_STEP) { curSpeed = 0; return; } curSpeed -= CAR_CONST::BRK_STEP; }
|
#include "Car.h" int main(void) { Car run99; run99.InitMembers("run99", 100); run99.Accel(); run99.Accel(); run99.Accel(); run99.ShowCarState(); run99.Break(); run99.ShowCarState(); return 0; } |
클래스 Car를 구성하는 멤버의 파악도 한결 수월해졌습니다. 정리가 되었습니다. 파일 분할에 익숙해지기 바랍니다. 파일이 분할된 예제를 계속해서 보다 보면, 파일분할의 기준을 자연스럽게 알게 됩니다. 파일의 분할을 분담스러워하지 않도록 합니다.
인라인 함수는 헤더파일에 함께 넣어야 합니다.
컴파일 과정에서 함수의 호출 문이 있는 곳에 함수의 몸체 부분이 삽입되어야 하므로. Break 함수가 인라인 함수가 아니라면, Break 함수가 Car 클래스의 멤버함수인지만 확인을 하고 컴파일은 완료가됩니다. 그러나 Break함수가 인라인 함수이기 때문에, Break 함수의 호출문장은 컴파일러에 의해서 Break 함수의 몸체로 대체되어야 합니다. 떄문에 인라인 함수는 클래스의 선언과 동일한 파일에 저장되어서 컴파일러가 동시에 참조할 수 있게 해야 합니다. 컴파일러는 파일 단위로 컴파일을 합니다. 그래서 클래스의 선언과 인라인 함수의 정의를 함께 묶어서 사용합니다.
객체 지향 프로그래밍의 이해
C언어에서 C++로 자연스럽게 이동할 수 있도록, 구조체를 시작으로 클래스를 설명하였습니다. 객체지향의 관점에서 클래스를 전혀 다른 방법으로, 다시 한번 설명하고 자합니다. 구조체를 확장한 것이 클래스라고 인식하는 것 자체는 문제가 되지 않으나, 그것이 전부는 아니기 때문입니다. C++는 객체 지향 언어입니다. 따라서 객체지향에 대한 이해가 필요한데, 이를 위해서 본인은 객체지향이 우월성을 강조할 것입니다. 즉, Object 는 우리 주변에 존재하는 물건이나 대상 전부를 의미합니다. 객체를 지향하는 프로그래밍은 무엇일까요? 나라는 객체는 과일장수라는 객체로부터 과일 객체의 구매라는 액션을 취할 수 있어야 합니다. 객체지향 프로그래밍에서는 나 그리고 과일장수와 같은 객체를 등장시킬 수 있을 뿐만 아니라, 나라는 객체가 과일장수라는 객체로부터 과일 객체를 구매하는 행위도 그대로 표현할 수 있습니다. 즉, 객체 지향 프로그래밍은 현실에 존재하는 사물과 대상, 그리고 그에 따른 행동을 있는 그대로 실체화 시키는 형태의 프로그래밍이다. 나와 과일장수라는 객체를 생성하여 다음의 행동을 실체화 시켜 보겠습니다.
객체를 이루는 것은 데이터와 기능입니다.
프로그래밍에서 바라보는 과일장수의 관점은 과일의 판매에 있습니다. 과일장수는 과일을 팝니다. 과일 장수는 사과 20개, 오렌지 10개를 보유하고 있습니다. 과일 장수는 과일 판매 수익은 현재까지 50,000원입니다. 객체는 하나 이상의 상태 정보(데이터)와 하나 이상의 행동(기능)으로 구성됩니다. 상태 정보는 변수를 통해서 표현이 되고, 행동은 함수를 통해서 표현이 됩니다.
과일 장수의 정의와 멤버변수의 상수화에 대한 논의
객체를 생성하기에 앞서 객체의 생성을 위한 틀을 먼저 만들어야 합니다. 이는 현실 세계에서 물건을 만들기 위해 틀을 짜는 행위에 비유할 수 있습니다. 사과의 가격을 의미하는 멤버 변수의 이름을 대문자로 구성한 이유는 사과의 판매가격은 일정하다 라는 가정의 결과입니다. 즉, 가격을 상수라고 가정한 것입니다. 보통 하루를 기준으로 사과의 판매가가 변하는 일은 없으니 상수로 가정하는 것도 일리는 있습니다. const 선언을 하여 실수로 값의 변경이 일어나느 일을 막았으면 좋겠으나, 이는 불가능하다. 클래스의 멤버 변수 선언문에서 초기화까지 하는 것을 허용하지 않기 떄문이빈다. 상수는 선언과 동시에 초기화되어야 하기 때문에 객체를 생성한 다음에 함수의 호출을 통해서라도 상수 값을 초기화하는 것은 안됩니다. 즉, 현재 변ㄴ수를 상수로 선언할 방법이 없습니다.
나를 표현하는 클래스의 정의
구매자에게 있어서 중요한 것은 소유하고 있는 현금의 액수와 소유하고 있는 사과의 수이다. 과일 구매자가 지녀야 할 기능은 과일의 구매이다. 따라서 이 기능을 담당할 함수를 클래스에 추가해야 합니다. 위의 클래스에 선언된 두 변수는 private 이나 public과 같은 선언이 존재하지 않음을 알 수 있습니다. 그러나 클래스는 아무런 선언이 존재하지 않을 때 private으로 간주된다고 하지 않았는가. 이 둘은 private 입니다.
클래스 기반의 두 가지 객체 생성 방법
이들은 실체가 아닌 틀이다. 따라서 접근도 호출도 불가능하다. 그럼 이제 해야 할 일은 앞서 정의한 클래스를 실체화시키는 것입니다. 즉, 객체화시키는 것입니다. C++에서는 객체생성 방법을 두 가지로 나타내고 있습니다. 이는 기본 자료형 변수의 선언 방식과 동일함을 보이는 것이니 당황할 필요는 없습니다. 일반적인 변수의 선언방식과 동적 할당방식이 있습니다.
#include <iostream> using namespace std; class FruitSeller { private: int APPLE_PRICE; int numOfApples; int myMoney; public: void InitMembers(int price, int num, int money) { APPLE_PRICE = price; numOfApples = num; myMoney = money; } int SaleApples(int money) { int num = money / APPLE_PRICE; numOfApples -= num; myMoney += money; return num; } void ShowSaleResult() { cout << "remaning apples: " << numOfApples << endl; cout << "Sale result: " << myMoney << endl << endl; } }; class FruitBuyer { int myMoney; int numOfApples; public: void InitMembers(int money) { myMoney = money; numOfApples = 0; } void BuyApples(FruitSeller &seller, int money) { numOfApples += seller.SaleApples(money); myMoney -= money; } void ShowBuyResult() { cout << "current price: " << myMoney << endl; cout << "Apples number: " << numOfApples << endl << endl; } }; int main(void) { FruitSeller seller; seller.InitMembers(1000, 20, 0); FruitBuyer buyer; buyer.InitMembers(5000); buyer.BuyApples(seller, 2000); cout << "Current Situation" << endl; seller.ShowSaleResult(); cout << "Buyer Situation" << endl; buyer.ShowBuyResult(); return 0; } |
클래스 내에서 접근제어 지시자가 생략되었으니, 이 둘은 private 으로 간주됩니다. 과일장수를 대상으로 과일의 구매를 목적으로 함수를 호출하고 있습니다. 이 예제를 보면 FruitBuyer 객체에 존재하는 함수 내에서 FruitSeller 객체의 함수 SaleApples를 호출하고 있습니다. 이 한 문장은 현실세계에서 다음과 같이 반영이 됩니다. seller님, 사과 2,000원어치 주세요. 객체지향에서 매우 중요한 의미를 갖습니다. 나라는 객체가 과일장수라는 객체로부터 과일 객체를 구매하는 행위도 그대로 표현할 수 있습니다. 이처럼 하나의 객체가 다른 하나의 객체에게 메시지를 전달하는 방법은(어떠한 행위의 요구를 위한 메시지 전달) 함수호출을 기반으로 합니다. 그래서 객체지향에서는 이러한 형태의 함수호출을 가리켜 메시지 전달이라고 합니다. 관계를 형성하는 둘 이상의 클래스 하나의 독립된 클래스를 정의하는 것은 쉽습니다. 그러나 둘 이상의 클래스를 정의한뒤, 관계를 형성해서 정의하는 것은 쉽지 않습니다. 하지만 이는 매우 중요합니다. 단순히 함수호출로 이해하면 별 것 아니지만, 메시지 전달의 관점에서 보면 이는 매우 중요합니다