728x90
728x90

구조체를 사용해야 되는 이유 , 클래스를 사용해야 되는 이유에 대해 정리해본다.


3.1 구조체와 클래스


구조체의 유용성 (WHY?)
struct Car basicCar; // 사용자정의자료형 구조체이름

구조체는 관련 있는 데이터를 하나의 자료형으로 묶을 수 있다. 
함께 움직이는 데이터들을 묶어주는 효과가 있다. -> 프로그램 구현, 관리가 쉬움

ex. 비디오대여가게에서 필요한 프로그램의 기능은?
- 고객관리, 비디오아이템관리(등록, 삭제, 대여 관련 정보), 회계관리
- 고객관리에서 회원은 각종 정보들과 함께 생성, 삭제되어 라이프사이클이 같다

소프트웨어 = 데이터의 표현 + 데이터의 처리 이며 표현해야 하는 데이터는 부류를 형성한다.
부류를 형성하는 데이터들은 함께 생성, 이동, 소멸되는 특성이 있다.
구조체는 이런 연관있는 데이터들을 묶을 수 있는 장치이다.

 

 

C언어의 구조체에 대한 불만

모든 사용자정의 자료형에 대한 불만, 기본 자료형으로 인식해주지 않는다. 
- 기본자료형 int a; 처럼 Person b;로 쓰면 에러 발생하여 struct를 앞에 붙여줘야 한다. -> 구분짓고 있다
- 기본자료형처럼 형태를 일치할 수 없는 지 의문

C++은 기본자료형과 사용자정의자료형을 동일한 자료형으로 인식한다.

또한 기본자료형은 각종 연산이 가능하다. C에서 사용자정의연산은 대입 외에 다른 연산이 되지 않는데,
C++은 사용자 정의 자료형도 기본자료형처럼 각종 연산이 가능하다.

// 사용자 정의 자료형 <-> 기본자료형
// 자료형이 Person이다. 
struct Person 
{
	int age;
	char name[10];
}

int main()
{
	int a = 10;
	Person p; //sturct Person p;
	return 0;
}
더보기
#include <iostream>
using namespace std; 

// 구조체 Car랑 관련된 정보의 상수화
#define ID_LEN 20
#define MAX_SPD 200
#define FUEL_STEP 2
#define ACC_STEP 10
#define BRK_STEP 10

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

// 차의 정보 출력 함수 
// 단순 정보 출력만 해서 const 참조자를 매개변수로 선언
// 참조자를 이용한 원본값의 변형은 하지 않겠다는 뜻
// https://gjghks.tistory.com/40
void ShowCarState(const Car &car) {
    cout << "소유자ID : " << car.gamerID << endl;
    cout << "연료량 : " << car.fuelGauge << "%" << endl;
    cout << "현재속도 : " << car.curSpeed << "km/s" << endl<< 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;
}

 

ShowCarState, Accel, Break 함수는 구조체 Car과 함께 부류를 형성해 데이터 처리를 담당하는 함수이다.
구조체 Car에 종속적인데도 전역함수로 선언되어 종속적임을 나타내지 못하고 있으며 다른데서 함수를 호출하는 실수가 생길 수도 있는 예이다.

 

 

함수를 넣으면 좋은 구조체

프로그램 = 데이터 + 데이터 조작 루틴(함수)
잘 구성된 프로그램은 데이터와 더불어 함수들도 부류를 형성한다.

 

더보기

구조체에 종속적인 함수들을 구조체 안에 넣어버리는 것..
데이터와 함수를 묶어벼러서 확실히 구분된다.
구조체 내에 선언된 변수에 직접접근이 가능하다

C++은 구조체 안에 함수를 넣는 것을 허용한다.

#include <iostream>
using namespace std; 

// 구조체 Car랑 관련된 정보의 상수화
#define ID_LEN 20
#define MAX_SPD 200
#define FUEL_STEP 2
#define ACC_STEP 10
#define BRK_STEP 10

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

void ShowCarState() {
    cout << "소유자ID : " << gamerID << endl;
    cout << "연료량 : " << fuelGauge << "%" << endl;
    cout << "현재속도 : " << 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;
}

 

 

 

ex. 고객정보 + 입출금함수

고객정보 

#include <iostream>
using namespace std;

struct Account{
    char accID[20];
    char secID[20];
    char name[20];
    int balance;
    // 계좌, 비번, 이름, 잔액
};

int main()
{
// struct 키워드를 붙이지 않았다. C와의 호환성을 위해 붙여도 된다.
    Account yoon={"1234", "2321", "yoon", 1000};
    cout<<yoon.accID<<endl;
    cout<<yoon.secID<<endl;
    cout<<yoon.name<<endl;
    cout<<yoon.balance<<endl;

    return 0;
}

 

 

고객정보와 입출금

#include <iostream>
using namespace std;

struct Account{
    char accID[20];
    char secID[20];
    char name[20];
    int balance;
    // 계좌, 비번, 이름, 잔액
};

// 입금
// C라면 포인터로 매개변수가 작성되었음도 생각해보자
void Deposit(Account &acc, int money) {
    acc.balance+=money;
}
// 출금
void Withdraw(Account &acc, int money) {
    acc.balance-=money;
}

int main()
{
    Account yoon={"1234", "2321", "yoon", 1000};
    
    cout<<yoon.accID<<endl;
    cout<<yoon.secID<<endl;
    cout<<yoon.name<<endl;
    cout<<yoon.balance<<endl;
    
    Deposit(yoon, 100);
    cout<<"입금 후"<<endl;
    cout<<yoon.balance<<endl;
    
    Withdraw(yoon, 200);
    cout<<"출금 후"<<endl;
    cout<<yoon.balance<<endl;
    

    return 0;
}

 

 

구조체 안에 멤버함수로 들어갔음

C는 에러가 발생하나, C++은 구조체 안에도 함수를 넣을 수 있다.
함수를 접근할 때 멤버변수로 접근한다. (.) 를 사용해서.

구조체 내부 멤버함수 안에 있는 변수는 멤버변수로 구조체 내에 있는 데이터들만 조작한다. 다른 용도로는 사용을 하지 않아 묶어주는 효과가 있다. 하나의 함수는 하나의 기능을 가지며, 함수에서 다루는 데이터들도 어떤 범주를 가져야 한다는 것도 생각해 볼 수 있겠다.

 

#include <iostream>
using namespace std;

struct Account{
    // 계좌, 비번, 이름, 잔액
    char accID[20];
    char secID[20];
    char name[20];
    int balance;

    // 구조체의 멤버 함수
    // 입금
    // C라면 포인터로 매개변수가 작성되었음도 생각해보자
    void Deposit(int money) {
        balance+=money;
    }
    // 출금
    void Withdraw(int money) {
      balance-=money;
    }
};

int main()
{
    Account yoon={"1234", "2321", "yoon", 1000};
    
    cout<<yoon.accID<<endl;
    cout<<yoon.secID<<endl;
    cout<<yoon.name<<endl;
    cout<<yoon.balance<<endl;
    
    yoon.Deposit(100);
    cout<<"입금 후"<<endl;
    cout<<yoon.balance<<endl;
    
    yoon.Withdraw(200);
    cout<<"출금 후"<<endl;
    cout<<yoon.balance<<endl;
    

    return 0;
}

 

 

 

구조체 안에 enum 상수의 선언

열거형 enum을 사용해서 구조체 내에서 유효한 상수를 정의한다.

더보기

앞 예제의 매크로 상수들 역시 구조체에 종속적이며 다른 곳에서 사용하도록 정의되지 않았으므로 구조체 내에 포함시키는 것이 낫다. 열거형 enum을 사용해서 구조체 내에서 유효한 상수를 정의한다.

#define ID_LEN 20
#define MAX_SPD 200
#define FUEL_STEP 2
#define ACC_STEP 10
#define BRK_STEP 10
#include <iostream>
using namespace std; 

struct Car
{
    // 열거형 enum 상수 선언
   enum
   {
    ID_LEN = 20,
    MAX_SPD = 200,
    FUEL_STEP = 2,
    ACC_STEP = 10,
    BRK_STEP = 10
   };

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

void ShowCarState() {
    cout << "소유자ID : " << gamerID << endl;
    cout << "연료량 : " << fuelGauge << "%" << endl;
    cout << "현재속도 : " << 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;
}

 

 

구조체 안에 이름공간을 이용해서 상수를 명시

가독성이 더욱 좋아지며 구조체들 사이에서만 사용하는 사용하는 상수들을 선언할 때 좋다.

#include <iostream>
using namespace std; 

// 이름 공간안에 구조체 Car에서 사용하는 상수를 모아놓음
namespace CAR_CONST {
    // 열거형 enum 상수 선언
    enum
    {
        ID_LEN = 20,
        MAX_SPD = 200,
        FUEL_STEP = 2,
        ACC_STEP = 10,
        BRK_STEP = 10
    };
}

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

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

 

구조체를 보는 순간 정의되어있는 함수의 종류와 기능이 한 눈에 보게끔 코드를 작성하는 게 좋다.
그래서 구조체 내에 정의된 함수의 수가 많거나 길이가 길다면 다음과 같이 구조체 밖으로 함수를 빼낼 수 있다.

 

struct Car
{      
    ...
void ShowCarState();
void Accel();
void Break();
	...
};
void Car::ShowCarState()
{
	...
}
void Car::Accel()
{
	...
}
더보기
#include <iostream>
using namespace std; 

// 이름 공간안에 구조체 Car에서 사용하는 상수를 모아놓음
namespace CAR_CONST {
    // 열거형 enum 상수 선언
    enum
    {
        ID_LEN = 20,
        MAX_SPD = 200,
        FUEL_STEP = 2,
        ACC_STEP = 10,
        BRK_STEP = 10
    };
}

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

};

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

 

구조체 안에 함수가 정의되어 있으면 함수를 인라인으로 처리해라는 의미가 들어있다. 예제처럼 함수를 구조체 외부로 빼면 이런 의미가 사라져서 인라인 처리하려면 키워드 inline 처리를 명시적으로 지시해야 한다.

 


2. 클래스와 객체


구조체가 아니고 클래스

C++의 사용자정의 자료형 클래스 : 변수, 함수까지 묶어서 자료형을 정의할 수 있다. 변수+함수
문법적으로 class를 붙여 사용
클래스는 기본적으로 private이다

구조체 : 기존자료형들로로 새로 자료형을 만든 것, 변수들로만 만들 수 있다.
문법적으로 struct를 붙여 사용
구조체는 기본적으로 public
그래서 어디서든 접근이 가능하다.

 

클래스 등장 이유 : -

클래스 = 멤버변수 + 멤버함수
클래스의 변수는 변수가 아니라 객체(object)라고 표현한다.

C++ 클래스 > 구조체 (범위)

 

 

사물 관찰 이후의 데이터 추상화

현실 세계의 사물을 데이터적 측면과 기능적인 측면을 통해 정의하는 것
데이터 (변수)+ 기능 (함수)

 

데이터 추상화 이후의 클래스화

추상화된 데이터를 갖고 사용자 정의 자료형을 정의하는 것 -> 클래스화

 

 

클래스화 이후의 인스턴스화 (객체화라고도 한다)

클래스 기반의 객체(Object) 생성

#include <iostream>
using namespace std;

class Account {
public:
    // 계좌, 비번, 이름, 잔액
    char accID[20];
    char secID[20];
    char name[20];
    int balance;

    // 구조체의 멤버 함수
    // 입금
    // C라면 포인터로 매개변수가 작성되었음도 생각해보자
    void Deposit(int money) {
        balance += money;
    }
    // 출금
    void Withdraw(int money) {
        balance -= money;
    }
};

int main()
{
    Account yoon = { "1234", "2321", "yoon", 1000 };

    cout << yoon.accID << endl;
    cout << yoon.secID << endl;
    cout << yoon.name << endl;
    cout << yoon.balance << endl;

    yoon.Deposit(100);
    cout << "입금 후" << endl;
    cout << yoon.balance << endl;

    yoon.Withdraw(200);
    cout << "출금 후" << endl;
    cout << yoon.balance << endl;


    return 0;
}

 

 

 


3-3. 클래스 멤버의 접근 제어


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

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

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>

using namespace std; 

// 이름 공간안에 구조체 Car에서 사용하는 상수를 모아놓음
namespace CAR_CONST {
    // 열거형 enum 상수 선언
    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(const char *ID, int fuel);
    void ShowCarState();
    void Accel();
    void Break();
};

void Car::InitMembers(const  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)
{  
    // run99는 객체
    Car run99;
    run99.InitMembers("run99", 100);
    run99.Accel();
    run99.Accel();
    run99.ShowCarState();
    run99.Break();
    run99.ShowCarState();
    
    return 0;
}

 

 

멤버 변수 : 클래스를 구성하는 (클래스 내에 선언된) 변수
멤버 함수 : 클래스를 구성하는 (클래스 내에 정의된) 함수

 

 

C언어 파일분할 복습

헤더파일의 역할 : cpp파일로 만들어진 obj 파일에 있는 함수들의 내용을 다른 cpp파일에서 사용할 수 있게 한다.
A.cpp파일에서 B.cpp파일에 있는 함수나 클래스를 사용하려면 함수의 프로토타입이나 클래스선언 등의 정보가 필요하다. (그래야 어떤 함수나 메소드 호출 시 인자값이 필요한지, 리턴타입이 뭔지 알 수 있다). + 관리와 공유가 편하다

C언어를 대상으로 헤더파일에 들어가야 할 내용 구분

헤더파일의 중복포함을 막기 위해서 사용하는 매크로 #ifndef~#endif : https://sexycoder.tistory.com/4

둘 이상의 파일을 컴파일해서 하나의 실행파일을 만드는 법
링커(Linker)가 하는 일 : https://luckyyowu.tistory.com/8

 

 

C++에서의 파일분할

클래스를 대상으로 파일을 나눌 때
1) 헤더파일은 클래스의 선언,
2) 코드파일은 클래스의 정의(멤버함수의 정의)
를 담는다.

클래스의 선언(declaration)은 컴파일러가 Car 클래스와 관련된 문장의 오류를 잡아내는데 필요한 최소한의 정보로 클래스를 구성하는 외형적인 틀을 보여준다.

// 클래스의 선언
class Car
{
private: 
    char gamerID[CAR_CONST::ID_LEN];
    int fuelGauge;
    int curSpeed;
public:
    void InitMembers(const char *ID, int fuel);
    void ShowCarState();
    void Accel();
    void Break();
};

 

클래스의 정의(definition)는 다른 문장의 컴파일에 필요한 정보가 없고, 컴파일 된 후에 링커에 의해 하나의 실행파일로 묶일 코드이다.

void Car::InitMembers(const  char *ID, int fuel) { ... }

 

클래스와 관련된 문장의 컴파일 정보로 사용되는 클래스의 선언은 헤더파일에 저장해서 필요한 위치에 쉽게 포함되도록 하며 클래스의 정의는 소스파일에 저장해서 컴파일되게 한다. 

 

전에 사용했던 예제를 3개의 파일로 나눠본 예

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];
	int fuelGauge;
	int curSpeed;
public:
	void InitMembers(const char* ID, int fuel);
	void ShowCarState();
	void Accel();
	void Break();
};

#endif

 

Car.cpp

멤버함수의 정의부분을 컴파일하는데도 클래스의 선언 정보가 필요하다. 멤버함수에서 접근하는 변수의 존재유무를 확인해야 되고, 이름공간 CAR_CONST에 선언된 상수의 사용을 위해서도 헤더파일이 포함되어야 한다.

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
#include "Car.h"
using namespace std; 


void Car::InitMembers(const  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;
}

 

 

인라인함수는 헤더파일에 같이 넣어야 한다.

Car.cpp에 정의된 ShowCarState, Break 등을 인라인화 할 경우 현재 위치에서는 컴파일에러 발생.
컴파일 과정에서 함수의 호출 문이 있는 곳에 함수의 몸체 부분이 삽입되어야 하므로
함수의 호출문장은 컴파일러에 의해서 함수의 몸체로 대체되어야 하기에 |

인라인함수는 클래스의 선언과 동일한 파일에 저장되어서 컴파일러가 동시에 참조할 수 있게 해야 한다.
즉 헤더파일에 같이 몸체를 넣어주면 됨.

컴파일러는 파일 단위로 컴파일을 하기 때문에
서로 참조되지 않기 때문에 클래스의 선언과 인라인 함수의 정의를 한 파일에 묶어두어야 하는 것이다.

 

 

클래스의 내부 접근과 외부 접근
#include <iostream>
using namespace std;

class Counter {
public:
    int val; //쓰레기값

    void Increment(void) {
        val++; //내부접근
    }
};

int main()
{
    Counter cnt; // 스택 공간 내에 지역 객체가 생성된다.
    // cnt라는 이름의 객체를 Counter라는 자료형으로 생성한다     

    cnt.val = 0; //외부접근
    // 쓰레기값에서 0이 된다

    cnt.Increment();
     
    cout << cnt.val << endl;
    return 0;
}

 

 

C++에서는 접근제어 키워드로 public, protected, private 3가지를 가진다. 접근허용 범위가 다른 것
public은 외부, 내부 접근 모두 허용하며(접근제어를 안한다) private 변수는 내부 접근만 허용한다.

protected는 상속 관련 내용이라 나중에 배우며, 클래스 멤버의 접근제어를 하는 이유는 다음 장에서 배운다. 

 

 

멤버 함수의 외부 정의
#include <iostream>
using namespace std;

const int OPEN = 1;
const int CLOSE = 2;

class Door {
    // struct, class의 차이점
    // 접근제어 키워드를 지우면 에러 발생 (private로 인식)
    // class 키워드를 struct로 바꿔도 에러는 없으나
    // private : 키워드를 넣으면 컴파일되나 
    // 접근제어 키워드를 생략하면 클래스는 프라이빗, 스트럭트는 퍼블릭으로 자동 붙음 (구조체와 유일한 차이점)
    // 클래스 사용을 지향한다 -- 인터페이스 기능을 작용된다 
private:
    int state;

public:
    void Open() {
        state = OPEN;
    }
    void Close() {
        state = CLOSE;
    }
    void ShowState() {
        cout << "현재 문의 상태 : ";
        cout << ((state == OPEN) ? "OPEN" : "CLOSE") << endl;
    }
};

int main()
{
    Door d;
    // d.state=OPEN; //private
    d.Open();
    d.ShowState();
    return 0;
}

 

 

#include <iostream>
using namespace std;

const int OPEN = 1;
const int CLOSE = 2;

class Door {
    // struct, class의 차이점
    // 접근제어 키워드를 지우면 에러 발생 (private로 인식)
    // class 키워드를 struct로 바꿔도 에러는 없으나
    // private : 키워드를 넣으면 컴파일되나 
    // 접근제어 키워드를 생략하면 클래스는 프라이빗, 스트럭트는 퍼블릭으로 자동 붙음 (구조체와 유일한 차이점)
    // 클래스 사용을 지향한다 -- 인터페이스 기능을 작용된다 
private:
    int state;

public:
    void Open() {
        state = OPEN;
    }
    void Close() {
        state = CLOSE;
    }
    void ShowState() {
        cout << "현재 문의 상태 : ";
        cout << ((state == OPEN) ? "OPEN" : "CLOSE") << endl;
    }
};

int main()
{
    Door d;
    // d.state=OPEN; //private
    d.Open();
    d.ShowState();
    return 0;
}

 

 

멤버 함수의 인라인화

C는 #define 매크로로 함수를 인라인시키고, C++는 inline 키워드로 인라인시킨다.

#include <iostream>
using namespace std;

const int OPEN = 1;
const int CLOSE = 2;

class Door {
    // struct, class의 차이점
    // 접근제어 키워드를 지우면 에러 발생 (private로 인식)
    // class 키워드를 struct로 바꿔도 에러는 없으나
    // private : 키워드를 넣으면 컴파일되나 
    // 접근제어 키워드를 생략하면 클래스는 프라이빗, 스트럭트는 퍼블릭으로 자동 붙음 (구조체와 유일한 차이점)
    // 클래스 사용을 지향한다 -- 인터페이스 기능을 작용된다 
private:
    int state;

public:
   /* void Open() {
        state = OPEN;
    }*/
    void Open();
    void Close() {
        state = CLOSE;
    }
    void ShowState() {
        cout << "현재 문의 상태 : ";
        cout << ((state == OPEN) ? "OPEN" : "CLOSE") << endl;
    }
};

// 전역함수가 아니라 클래스 내 멤버함수를 정의할 때 이렇게 쓴다
void Door::Open() {
    state = OPEN;
}

int main()
{
    Door d;
    // d.state=OPEN; //private
    d.Open();
    d.ShowState();
    return 0;
}

 

 

 


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


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

객체는 1개 이상의 상태정보(데이터)와 1개 이상의 행동(기능)으로 구성된다.
데이터는 변수로 표현이 되고, (상태정보저장) 행동은 함수로 표현이 된다.

객체 생성은 변수 선언, 함수 정의가 들어간 틀이 필요하다. 그게 클래스이다.
틀을 만들고 나서 정의한 클래스를 실체화를 시켜야 객체다. 아래는 클래스 기반의 객체 생성 방법 2가지이다.

ClassName objName; 
ClassName * ptrObj = new Classname; //힙 할당방식(동적할당)

 

예제

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

 

 

 

message passing, 객체간의 대화 방법

어떤 동작의 요구를 위해 한 객체에 다른 객체에게 메세지를 전달하는 것은 함수호출을 기반으로 한다.
이것을 메세지전달이라고 한다.

 

 

반응형

 

 


연습문제


03-1 구조체 내에 함수정의하기
struct Point
{
	int xpos;
	int ypos;
}

2차원 평면상에서의 좌표를 표현할 수 있는 구조체를 정의했다. 이 구조체를 기반으로 다음의 함수를 정의하고자 한다.
단 함수들을 구조체 안에 정의해서 다음의 형태로 main 함수를 구성할 수 있어야 한다.

void MovePos(int x, int y); //점의 좌표이동
void AddPoint(const Point &pos); //점의 좌표증가
void ShowPosition(); //현재 x,y 좌표정보 출력

실행결과는 아래와 같게끔 정의한다.

[5, 14]
[25, 44]

 

 

푼 것

#include <iostream>
using namespace std;

// 2차원에서 좌표를 구현하는 구조체
struct Point
{
	int xpos;
	int ypos;

	//점의 좌표이동
	void MovePos(int x, int y) {
		xpos += x;
		ypos += y;
	}
	
	//점의 좌표증가
	void AddPoint(const Point& pos) {		
		/*cout << "[" << xpos << "," << ypos << "]" << endl;
		cout << "[" << pos.xpos << "," << pos.ypos << "]" << endl;*/

		xpos += pos.xpos;
		ypos += pos.ypos;
	}

	//현재 x,y 좌표정보 출력
	void ShowPosition() {
		cout << "[" << xpos  << "," << ypos  << "]" << endl;
	}
};

int main(void) 
{
	Point pos1 = { 12, 4 };
	Point pos2 = { 20, 30 };

	pos1.MovePos(-7, 10);
	pos1.ShowPosition(); //[5, 14] 출력

	pos1.AddPoint(pos2);
	pos1.ShowPosition(); //[25, 44]] 출력
	
	return 0;
}

 

 

03-2 클래스의 정의

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 <iostream>
using namespace std;

class Calculator {
private: 
	int AddCount;
	int SubCount;
	int MulCount;
	int DivCount;
public:
	void Init() {
		AddCount = 0;
		SubCount = 0;
		MulCount = 0;
		DivCount = 0;
	}
	// 사칙연산
	double Add(double x, double y) {		
		AddCount++;  
		return x + y;
	}
	double Sub(double x, double y) {
		SubCount++;
		return x - y;
	}
	double Mul(double x, double y) {
		MulCount++;
		return x * y;
	}
	double Div(double x, double y) {
		DivCount++;
		return x / y;
	}
	// 연산 횟수 기록
	 void ShowOpCount() {
		cout << "덧셈: " << AddCount <<endl;
		cout << "뺄셈: " << SubCount << endl;
		cout << "곱셈: " << MulCount << endl;
		cout << "나눗셈: " << DivCount << 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.Sub(2.2, 1.5) << endl;
	cout << "4.9 / 1.2 = " << cal.Div(4.9, 1.2) << endl;
	  
	cal.ShowOpCount();
	return 0;
}

 

 

답안

#include <iostream>
using namespace std;

class Calculator {
private: 
	int AddCount;
	int SubCount;
	int MulCount;
	int DivCount;
public:
	void Init();
	double Add(double num1, double num2);
	double Sub(double num1, double num2);
	double Mul(double num1, double num2);
	double Div(double num1, double num2);
	void ShowOpCount();
};

void Calculator::Init() {
	AddCount = 0;
	SubCount = 0;
	MulCount = 0;
	DivCount = 0;
}
// 사칙연산
double Calculator::Add(double x, double y) {
	AddCount++;  
	return x + y;
}
double Calculator::Sub(double x, double y) {
	SubCount++;
	return x - y;
}
double Calculator::Mul(double x, double y) {
	MulCount++;
	return x * y;
}
double Calculator::Div(double x, double y) {
	DivCount++;
	return x / y;
}
// 연산 횟수 기록
	void Calculator::ShowOpCount() {
	cout << "덧셈: " << AddCount <<endl;
	cout << "뺄셈: " << SubCount << endl;
	cout << "곱셈: " << MulCount << endl;
	cout << "나눗셈: " << DivCount << 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.Sub(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) 
{
	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 <iostream>
using namespace std;

class Printer {
private:
	char pntString[30];
public:
	void SetString(const char *s1);
	void ShowString();
};

void Printer::SetString(const char *s1) {		
	//"const char *" 형식의 인수가 "char *" 형식의 매개 변수와 호환되지 않습니다.
	// 함수의 매개변수에 const를 붙여준다.
    
    // 문자열복사
	strcpy(pntString, s1);
}

void Printer::ShowString() {
	cout << pntString << endl;
}

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

	pnt.SetString("I Love C++");
	pnt.ShowString();

	return 0;
}
728x90
728x90
블로그 이미지

coding-restaurant

코딩 맛집에 방문해주셔서 감사합니다.

,

v