본문 바로가기

C++/윤성우의 열혈 프로그래밍 C++

윤성우의 열혈 C++ chpt4.

C++ 에서의 구조체

구조체의 등장 배경

  • 연관 있는 데이터를 하나로 묶으면 프로그램의 구현 및 관리가 용이
  • 구조체는 연관 있는 데이터를 하나로 묶는 문법정 장치

연관 있는 데이터들은 생성 및 소멸의 시점이 일치하고, 이동 및 전달의 시점 및 방법이 일치하기 때문에 하나의 자료형으로 묶어서 관리하는 것이 용이하다.

 

 

C++ 에서의 구조체 변수 선언

// C 구조체 변수 초기화

struct Car basicCar;
struct Car simpleCar;

-------------------------------------

// C++ 구조체 변수 초기화

Car basicCar;
Car simpleCar;

C++ 에서는 구조체 변수 선언시 struct 키워드의 생략을 위한 typedef 선언이 불필요

 

struct Car
{
    char gamerID[ID_LEN];	// 소유자 ID
    int fuelGauge;		// 연료량
    int curSpeed;		// 현재속도
};

void ShowCarState(const Car &car)
{
    ...
}
void Accel(Car &car)
{
    ...
}
void Break(Car &car)
{
    ...
}

데이터 뿐만 아니라, 해당 데이터와 연관된 함수 모임들도 함께 그룹을 형성하기 때문에 함수도 하나로 묶는 것에 대해 나름의 가치를 부여할 수 있다

 

구조체 안에 함수 삽입하기

struct Car
{
    char gamerID[ID_LEN];
    int fuelGauge;
    int curSpeed;

    void ShowCarState(const Car &car)
    {
        ...
    }
    void Accel(Car &car)
    {
        // 상위에 선언된 구조체 변수에 접근
        cout << "소유자ID: " << gamerID << endl;
        cout << "연료량: " << fuelGauge << "%" << endl;
        cout << "현재속도: " << curSpeed << "km/s" << endl << endl;
    }
    void Break(Car &car)
    {
        if (curSpeed < BRK_STEP)
        {
            // 상위에 선언된 구조체 변수에 접근
            curSpeed = 0;
            return ;
        }
        curSpeed -= BRK_STEP;
    }
}

이렇게 구조체 내에서 함수를 정의하면, 외부에서 구조체를 통해 함수를 호출할 수 있다

구조체 내에서 정의된 함수는 inline 선언된 것으로 간주되어 컴파일 과정에서 inline 화 한다.

Car myCar;
myCar.ShowCarState();
myCar.Accel();
myCar.Break();

 

C++에서는 구조체 안에 함수를 삽입하는 것이 가능하다. 따라서 C++ 에서는 구조체가 아닌, 클래스라 한다.

 

실제로 cppreference.com 에서도

더보기

"A class defined with the keyword class has private access for its members and its base classes by default.

A class defined with the keyword struct has public access for its members and its base classes by default."


'구조체 키워드로 정의된 클래스는' .... 이라고 표현하고 있다.

 

 

C++ 에서의  구조체 변수 선언

실제로는 구조체 변수마다 함수가 독립적으로 존재하는 구조는 아니다.

그러나 논리적으로는 독립적으로 존재하는 형태로 보아도 문제가 없으니, 위의 그림의 형태로 변수(객체)를 이해하자

(C 로 비유하자면 같은 함수를 각각의 변수에 적용시킨 것과 유사)

 

 

구조체 안에 enum 상수의 선언

// Car 클래스를 위해 정의된 상수, 기존 define 방식
#define ID_LEN 20
#define MAX_SPD 200
#define FUEL_STEP 2
#define ACC_STEP 10
#define BRK_STEP 10
-----------------------------------------------------------
// namespace 를 활용하는 방식
namespace CAR_CONST
{
    enum
    {
        ID_LEN = 20,
        MAX_SPD = 200,
        FUEL_STEP = 2,
        ACC_STEP = 10,
        BRK_STEP = 10
    };
}
------------------------------------------------------------
// 구조체를 활용하는 방식
struct Car
{
    enum
    {
        ID_LEN = 20,
        MAX_SPD = 200,
        FUEL_STEP = 2,
        ACC_STEP = 10,
        BRK_STEP = 10
    };
    
    char gamerID[ID_LEN];
    int fuelGauge;
    int curSpeed;

    void ShowCarState() { ... }
    void Accel() { ... }
    void Break() { ... }
}

구조체 안에 enum 을 선언을 둠으로써 잘못된 외부의 접근을 제한할 수 있다.

 

 

함수는 외부로 뺄 수 있다.

struct Car
{
    ...
    void ShowCarState();
    void Accel();
    void Break();
    ...
}

void Car::ShowCarState() { ... }
void Car::Accel() { ... }
void Car::Break() { ... }
-----------------------------------------

// 함수 내 정의가 inline 적용이 되니 외부 정의시 inline 을 명시해서 사용하는 것을 권장!!!
inline void Car::ShowCarState() { ... }
inline void Car::Accel() { ... }
inline void Car::Break() { ... }

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

class Car
{
    char gamerID[ID_LEN];
    int fuelGauge;
    int curSpeed;

    void ShowCarState() { ... }
    void Accel() { ... }
    void Break() { ... }
}

int main(void)
{
    Car run99;
    strcpy(run99.gamerID, "run99");	// 무효, 접근불가
    run99.fuelGauge=100;		// 무효, 접근불가
    run99.curSpeed=0;			// 무효, 접근불가
    ...
}

단순히 키워드만 class로 바꾸면 선언된 멤버의 접근이 불가능하다.
따라서 별도의 접근제어와 관련된 선언을 추가 해야 한다.

 

 

접근제어 지시자

  • public : 어디서든 접근허용
  • protected : 상속관계에 놓여있을 때, 유도 클래스에서의 접근 허용
  • privare : 클래스 내(클래스 내에 정의된 함수)에서만 접근허용
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();
};

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

Car의 멤버함수는 모두 public이므로 클래스의 외부에 해당하는 main 함수에서 접근가능하다.

 

 

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

class Car
{
private:
    char gamerID[CAR_CONST::ID_LEN];
    int fuelGauge;
    int curSpeed;	// Car 클래스 내부에서 생성된 변수를 '멤버변수' 라고 한다.
    
public:
    void InitMembers(char *ID, int fuel);
    void ShowCarState();
    void Accel();
    void Break();	// Car 클래스 내부에서 정의/선언된 함수를 '멤버함수' 라고 한다.
};

int main(void)
{
    Car run99;		// Car 클래스 대상으로 생성된 변수는 '객체' 라고 한다.
    ...
}

 

 

 

C++ 에서의 파일 분할

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

클래스의 선언은 일반적으로 헤더파일에 삽입한다. 객체생성문 및 멤버의 접근문장을 컴파일하기 위해서 필요하다. 
클래스의 이름을 때서 Car.h로 헤더파일의 이름정의하기도 한다.

 

 

단! '인라인 함수'는 컴파일 과정에서 함수의 호출문을 대체해야 하기 때문에 헤더파일에 함께 정의되어야 한다

 

 

void Car::InitMembers(char *ID, int fuel) { ... }
void Car::ShowCarState() { ... }
void Car::Accel() { ... }
void Car::Break() { ... }

Car 클래스의 멤버함수의 몸체는 다른 코드의 컴파일 과정에서 필요한 게 아니다. 

링크의 과정을 통해서 하나의 바이너리로 구성만 되면 된다. 따라서 cpp 파일에 정의하는 것이 일반적이다.
클래스의 이름을 따서 Car.cpp로 소스파일의 이름을 정의하기도 한다.


객체지향 프로그래밍의 이해

객체에 대한 간단한 정의

  • 사전적 의미 : 물건 또는 대상
  • 객체지향 프로그래밍 : 객체 중심의 프로그래밍

객체지향 프로그래밍에서는 '나', '과일장수', '사과'라는 객체를 등장시켜서 두 개의 사과 구매라는 행위를 실체화한다.

 

객체지향 프로그래밍은 현실에 존재하는 사물과 대상, 그리고 그에 따른 행동을 있는 그대로 실체화시키는 형태의 프로그래밍이다.

 

 

객체를 이루는 것은 데이터와 기능

 

 

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 ShowSalesResult()
    {
        cout << "남은 사과: " << numOfApples << endl;
        cout << "판매 수익: " << myMoney << endl;
    }
}

class FruitBuyer
{
    int myMoney;	// private
    int numOfApples	// private
    // 따로 private 선언을 하지 않아도 기본 private 적용이나 웬만하면 private 선언해 줄 것을 권장
public:
    void InitMembers(int money)
    {
        myMomey = momey;
        numOfApples = 0;
    }
    void BuyApples(FruitSeller &seller, int money)
    {
        numOfApples += seller.SaleApples(money);
        myMoney -= money;
    }
    void ShowBuyResult()
    {
        cout << "현재 잔액: " << myMoney << endl;
        cout << "사과 개수: " << numOfApples << endl;
    }
}

int main(void)
{
    FruitSeller seller;
    seller.InitMembers(1000, 20, 0);

    FruitBuyer buyer;
    buyer.InitMembers(5000);
    buyer.BuyApples(seller, 2000);

    cout << "과일 판매자의 현황" << endl;
    seller.ShowSalesResult();
    cout << "과일 구매자의 현황" << endl;
    buyer.ShowbuyResult();

    return 0;
}

 

 

'과일장수'의 정의와 멤버변수의 상수화

 

즉, 값이 정해지지 않은 상태에서의 const 상수화는 어렵다는 내용

 

 

‘나(me)’를 표현하는 클래스의 정의와 객체생성

 

 

 

사과장수 시뮬레이션 완료

'C++ > 윤성우의 열혈 프로그래밍 C++' 카테고리의 다른 글

윤성우의 열혈 C++ chpt6.  (2) 2025.06.08
윤성우의 열혈 C++ chpt5.  (0) 2025.05.16
윤성우의 열혈 C++ chpt3.  (0) 2025.05.12
윤성우의 열혈 C++ chpt2.  (0) 2025.05.11
윤성우의 열혈 C++ chpt1.  (0) 2025.05.11