14. [C++] 클래스(Class)와 객체(Object)

2024. 4. 12. 14:18[C++]/C++ 언어 기초

클래스와 구조체의 유일한 차이점

키워드 struct를 대신해 class를 사용하면, 구조체가 아닌 클래스가 된다. 

class Car {
    char gamerID[CAR_CONST::ID_LEN];
    int fuelGauge;
    int curSpeed;
    
    void ShowCarState() { . . . . }
    void Accel() { . . . . }
    void Break() { . . . . }
  • 키워드 struct가 아닌 class를 사용한 것이 코드상에서의 유일한 차이점이다.
  • 그런데 이렇게 class로 키워드를 바꿔놓으면 변수(구조체 변수)를 선언하지 못한다.

 

Car run99 = {"run99", 100, 0}; // (X)
  • 이유는 클래스 내에 선언된 함수에서가 아닌, 다른 영역에서 변수를 초기화하려 했기 때문이다.
  • 클래스 내에 선언된 변수는 클래스 내에 선언된 함수에서만 접근 가능하다. (별도의 선언을 하지 않으면)
Car run99; // (O)

그럼 어떻게 초기화를 어떻게 하는가? 

  • 클래스는 정의를 하는 과정에서 각각의 변수 및 함수의 접근 허용범위를 별도로 선언해야 한다.
  • 그럼 '접근제어 지시자'에 대해서 알아야 한다.

 


접근제어 지시자(접근제어 레이블)

C++의 접근제어 지시자는 다음과 같이 세 가지가 존재한다.

  • public : 어디서든 접근허용
  • protected : 상속관계에 놓여있을 때, 유도 클래스에서의 접근허용(상속)
  • private : 클래스 내(클래스 내에 정의된 함수)에서만 접근허용

RacingCarClassBase.cpp

#include <iostream>
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];	// 소유자 ID
	int fuelGauge;		// 연료량
	int curSpeed;		// 현재속도
    public:
        void InitMembers(char * ID, int fuel);
        void ShowCarState();
        void Accel();
        void Break();
};

void Car::InitMembers(char * ID, int fuel)
{
    strcpy(gamerID, ID);
    fuelGauge=fuel;
    curSpeed = 0;
}

void Car::ShowCarState()
{
	cout<<"소유자ID: "<<gamerID<<endl;
	cout<<"연료량: "<<fuelGauge<<"%"<<endl;
	cout<<"현재속도: "<<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.InitMembers("run99", 100);
    run99.Accel();
    run99.Accel();
    run99.ShowCarState();
    run99.Break();
    run99.ShowCarState();
    return 0;
}
소유자ID: run99
연료량: 96%
현재속도: 20km/s

소유자ID: run99
연료량: 96%
현재속도: 10km/s

 

  • Car Class private 아래에 있는 변수들은 class 내부에서만 접근이 가능해졌고,
  • public 아래에 있는 함수들은 어디서든지 접근이 가능해졌다.
  • 이러한 접근제어 지시자를 지정하지 않으면 디폴트 값은 private이기에 모든 변수, 함수들이 class 내부에서만 접근이 가능해지는 것이다.
void Car::InitMembers(char * ID, int fuel)
{
    strcpy(gamerID, ID);
    fuelGauge=fuel;
    curSpeed = 0;
}
  • private선언으로 인해 class밖에서는 초기화를 할 수 없으므로 변수의 초기화를 목적으로 위의 InitMembers 함수를 class 내부에서 선언하고 class 외부에 정의했다.
  • main함수에서는 이 함수의 호출을 통해 클래스 내부 선언된 변수를 초기화할 수 있게 된 것이다.

용어정리: 객체(Object), 멤버변수, 멤버함수

  • RacingCarClassBase.cpp의 run99는 '변수'가 아닌 '객체'라고 표현을 바꿔야 한다.
  • 구조체 변수, 클래스 변수라는 표현은 이제 어울리지 않는다. 구조체와 클래스는 변수의 성격만 지니는 것이 아니기 때문이다.
  • 변수라는 표현을 대신해 '객체(Object)'라는 표현을 사용한다.
  • 클래스를 구성하는(클래스 내에 선언된) 변수를 '멤버변수'라 한다.
char gamerID[CAR_CONST::ID_LEN];	
int fuelGauge;		
int curSpeed;		

 

클래스를 구성하는(클래스 내에 정의된) 함수를 '멤버함수'라 한다.

void InitMembers(char * ID, int fuel);
void ShowCarState();
void Accel();
void Break();

C++에서의 파일분할

C++에서의 파일분할에 대해 이야기해 보자. 클래스 Car을 대상으로 파일을 나눌 때에는 보통 다음과 같이 파일을 구분한다.

  • Car.h : 클래스의 선언을 담는다.
  • Car.cpp : 클래스의 정의(멤버함수의 정의)를 담는다.

 

클래스의 선언

class Car
{
    private:
        char gamerID[CAR_CONST::ID_LEN];	// 소유자 ID
	int fuelGauge;		// 연료량
	int curSpeed;		// 현재속도
    public:
        void InitMembers(char * ID, int fuel);
        void ShowCarState();
        void Accel();
        void Break();

};
  • Car 클래스와 관련된 문장의 오류를 잡아내는데 필요한 최소한의 정보로써, 클래스의 외형적인 틀을 보여준다.
  • 이를 '클래스의 선언(declaration)'이라 한다.

 

클래스의 정의

void Car::InitMembers(char *ID, int fuel) { . . . . }
void Car::ShowCarState() { . . . . }
void Car::Accel() { . . . . }
void Car::Break() { . . . . }
  • '클래스의 정의(definition)'에 해당하는 다음 함수의 정의는 다른 문장의 컴파일에 필요한 정보를 가지고 있지 않다.
  • 따라서 함수의 정의는 컴파일 된 이후에, 링커에 의해 하나의 실행파일로 묶이기만 하면 된다.
결론은, '클래스의 선언'은 헤더파일에, '클래스의 정의'는 소스파일에 저장한다.

 

파일 분할 예시

 

Car.h

#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];	// 소유자 ID
	    int fuelGauge;		// 연료량
	    int curSpeed;		// 현재속도
    public:
        void InitMembers(char * ID, int fuel);
        void ShowCarState();
        void Accel();
        void Break();

};
#endif __CAR_H__

 

Car.cpp

#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<<"연료량: "<<fuelGauge<<"%"<<endl;
	cout<<"현재속도: "<<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;
}

RacingMain.cpp

#include "Car.h"

int main(void) {
    Car run99;
    run99.InitMembers("run99", 100);
    run99.Accel();
    run99.Accel();
    run99.ShowCarState();
    run99.Break();
    run99.ShowCarState();
    return 0;
}
소유자ID: run99
연료량: 96%
현재속도: 20km/s

소유자ID: run99
연료량: 96%
현재속도: 10km/s


인라인 함수는 헤더파일에 함께 넣어야 해요.

  • 인라인 함수는 컴파일을 할 때, 함수의 호출부분을 몸체로 바꿔버린다.
  • 따라서 선언과 동시에 정의가 필요하다. 이는 따로 분리를 하면 안된다는 말이다.
  • 결론은 인라인 함수 선언과 정의를 한꺼번에 헤더파일(.h) 안에 통째로 넣어야 한다.

 

CarInline.h

#ifndef __CAR_H__
#define __CAR_H__

#include <iostream>
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];	// 소유자 ID
	int fuelGauge;		// 연료량
	int curSpeed;		// 현재속도
    public:
        void InitMembers(char * ID, int fuel);
        void ShowCarState();
        void Accel();
        void Break();

};

inline void Car::ShowCarState()
{
	cout<<"소유자ID: "<<gamerID<<endl;
	cout<<"연료량: "<<fuelGauge<<"%"<<endl;
	cout<<"현재속도: "<<curSpeed<<"km/s"<<endl<<endl;
}

inline void Car::Break()
{
    if(curSpeed<CAR_CONST::BRK_STEP)
    {
        curSpeed=0;
        return;
    }

    curSpeed-=CAR_CONST::BRK_STEP;
}
#endif __CAR_H__

 

CarInline.cpp

#include <cstring>
#include "CarInline.h"
using namespace std;

void Car::InitMembers(char * ID, int fuel)
{
    strcpy(gamerID, ID);
    fuelGauge=fuel;
    curSpeed = 0;
}

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

RacingInlineMain.cpp

#include "CarInline.h"

int main(void) {
    Car run99;
    run99.InitMembers("run99", 100);
    run99.Accel();
    run99.Accel();
    run99.ShowCarState();
    run99.Break();
    run99.ShowCarState();
    return 0;
}

 


실습 문제

 

문제 1

계산기 기능의 Calculator 클래스를 정의해보자. 기본적으로 지니는 기능은 덧셈, 뺄셈, 곱셈 그리고 나눗셈이며, 연산을 할 때마다 어떠한 연산을 몇 번 수행했는지 기록되어야 한다. 아래의 main 함수와 실행의 예에 부함하는 Calculator 클래스를 정의하면 된다. 단, 멤버변수는 private으로, 멤버함수는 public으로 선언하자.

int main(void)
{
    Calculator cal;
    cal.Init();
    cout << "3.2+2.4= "<< cal.Add(3.2, 2.4) << endl;
    cout << "3.5/1.7="<< cal.Div(3.5, 1.7) << endl;
    cout << "2.2-1.5="<< cal.Min(2.2, 1.5) << endl;
    cout << "4.9/1.2="<< cal.Div(4.9, 1.2) << endl;
    cal.ShowOpCount();
    return 0;
}

 

 

내 풀이

#include "stdafx.h"
#include <iostream>
#include <cstring>

using namespace System;
using namespace std;

class Calculator{
	private:
		int Add_num;
		int Min_num;
		int Multi_num;
		int Div_num;
	public:
		void Init();
		double Add(double num1, double num2);
		double Min(double num1, double num2);
		double Multi(double num1, double num2);
		double Div(double num1, double num2);
		void ShowOpCount();
};

void Calculator::Init()
{
	Add_num=0;
	Min_num=0;
	Multi_num=0;
	Div_num=0;
}


double Calculator :: Add(double num1, double num2){
	Add_num++;
	return num1 + num2;
}

double Calculator::Min(double num1, double num2){
	Min_num++;
	return num1 - num2;
}

double Calculator::Multi(double num1, double num2){
	Multi_num++;
	return num1 * num2;
}

double Calculator::Div(double num1, double num2){
	Div_num++;
	return num1 / num2;
}

void Calculator::ShowOpCount(){
	cout<< "덧셈: " << Add_num << " 뺄셈: "<<Min_num << " 곱셈: " << Multi_num<< " 나눗셈: " <<Div_num << endl;

}

int main (void)
{
	Calculator cal;
	cal.Init();
	cout << "3.2 + 2.4 = "<< cal.Add(3.2, 2.4) << endl;
	cout << "3.5 / 1.7 = "<< cal.Div(3.5, 1.7) << endl;
	cout << "2.2 - 1.5 = "<< cal.Min(2.2, 1.5) << endl;
	cout << "4.9 / 1.2 = "<< cal.Div(4.9, 1.2) << endl;
	cal.ShowOpCount();
	return 0;
}

 

문제 2

문자열 정보를 내부에 저장하는 printer라는 이름의 클래스를 디자인하자.

이 클래스의 두 가지 기능은 다음과 같다

1.문자열 저장

2.문자열 출력

 

아래의 main 함수와 실행의 예에 부합하는 Printer 클래스를 정의하되,이번에도 역시 멤버변수는 private, 멤버함수는 public으로 선언하자

int main(void)
{
        Printer pnt;
        pnt.SetString("Hello world!");
        pnt.ShowString();

        pnt.SetString("I love C++");
        pnt.ShowString();
        return 0;
}

 

 

내 풀이

#include "stdafx.h"
#include <iostream>
#include <cstring>

using namespace System;
using namespace std;

class Printer{
	private:
		char str1[30];
	public:
		void SetString(char* str);
		void ShowString();
};

void Printer :: SetString(char* str){
	strcpy(str1, str);
}


void Printer::ShowString(){
	cout<<str1<<endl;

}

int main (void)
{
	Printer pnt;
	pnt.SetString("Hello world!");
	pnt.ShowString();

	pnt.SetString("I love C++");
	pnt.ShowString();
	return 0;
}
반응형