본문 바로가기
C++/클래스

[C++ 클래스의 이해] 1. 클래스와 객체지향이란 무엇인가

by bigpicture 2022. 8. 19.
반응형

클래스가 무엇인지 이해하려면 원시 형태의 구조체 부터 시작해야 합니다. 사용자의 필요에 의해 구조체가 고안되고 발전한 역사적 맥락을 알아야 클래스를 제대로 이해할 수 있습니다. 

모든 언어의 빅 히스토리를 다루려는 것은 아닙니다. 구조체와 클래스의 한정하여 설명할 것입니다. 백 퍼센트 정확한 역사적 맥락을 알 수 없고 제 지식의 한계도 있기 때문에 어느정도 상상력을 가미하여 흐름을 구성하였습니다. 

구조체의 탄생

단일 자료를 관리할 때는 변수를 사용합니다. 한 사람의 키 정보를 입력하고 싶다면 변수 하나로 족합니다. 

float A_height=187.5;


개발자들은 같은 타입의 자료들을 관리하기 위해 배열이라는 것을 고안했습니다. 다섯 사람의 키는 아래와 같이 선언합니다. 각각을 변수로 선언하는 것 보다 훨씬 효율적입니다. 

float A_arr[5]={187.5,157,156,165,185};


여러 타입의 자료들을 묶어서 관리해야하는 경우가 빈번히 발생했고 개발자들은 구조체라는 것을 고안하게 됩니다. 어떤 사람의 다양한 정보를 관리하는 구조체는 아래와 같이 선언합니다. 

struct person
{
    char sex;
    int age;
    char job[20];
    float height;
    float weight;
};


성별,나이,직업,키,몸무게를 '맴버'로 갖는 구조체입니다. 클래스 안에 입력된 변수를 맴버라고 부릅니다. 

이해를 돕기 위해 용어를 좀 더 설명하겠습니다. 구조체는 일종의 틀입니다. 구조체만 선언한 상태에서는 어떤 사람의 실제 데이터가 입력된 것은 아닙니다. 빈칸으로 이루어진 서식입니다. 아래와 같이 어떤 사람의 실제 데이터를 입력하여 변수를 선언할 수 있습니다. 

person id1={"김지영",'F',23,"학생",164,56};


이때 id1 을 구조체 변수라고 부릅니다. 

 

구조체의 진화

개발자들은 구조체를 고안해내고 한결 편하게 코딩을 했을 것입니다. 그러다 이런 생각을 하게 됩니다. 

구조체 맴버들을 가지고 연산을 수행하는 함수를 구조체 안에 정의하면 어떨까? 

처음에는 간단한 함수를 정의해서 사용했을 것입니다. 키와 몸무게를 이용하여 체질량지수(BMI)를 구하는 함수를 추가해보면 아래와 같습니다. 아래 코드의 BMI( ) 입니다. 

struct person
{
    char name[20];
    char sex;
    int age;
    char job[20];
    float height;
    float weight;
    
    float BMI(){
        float bmi=weight/((height*0.01)*(height*0.01));
        return bmi;
    }
};


개발자들은 구조체를 사용하면서, 구조체변수들 간에 상호작용을 하게 만들 수 있을 것 같다는 생각을 하기 시작합니다. 

구조체변수들 간의 상호작용이 무엇인지 예를 들어보겠습니다. 위에서 정의한 person 구조체를 헬스장에서 사용한다고 합시다. 각 사람마다 헬스장 1일 이용권을 묶음으로 구매해서 사용하는 헬스장입니다. 회원들은 자신이 가진 이용권을 타인에게 양도할 수 있습니다. 어떤 회원이 현재 몇개의 회원권을 갖고 있는지를 실시간으로 관리하고 싶은데요. 헬스장 이용권 개수를 구조체에 입력해놓고, 서로 이용권을 주고 받을 때 마다 이용권 개수가 업데이트 되는 함수를 정의할 수 있습니다. 

이러한 개념을 확장하면 원하는 모든 것을 구조체변수로 만들 수 있습니다. 실제로 그렇게 코딩을 하는 개발자들이 생겨나기 시작했고 이는 하나의 패러다임이 됩니다. 

 

클래스와 객체의 탄생

구조체의 기능은 계속 확장되었고 정확한 시점을 딱 잘라 말할 수는 없지만 어느 순간 위에서 말한 새로운 패러다임이 등장하였습니다. 구조체라는 이름을 그대로 사용해도 됩니다만, 새로운 패러다임에는 새로운 이름을 붙이고 싶었습니다. 

개발자들은 구조체에 여러 기능을 추가한 확장된 구조체에 클래스라는 이름을 붙였습니다. 구조체 변수라는 말 대신 객체라는 이름을 사용하기로 했습니다. 

 

구조체  → 클래스 (Class)

구조체변수 → 객체 (Object)

개발자들은 클래스라는 이름을 통해 '새로운 패러다임'이 도래함을 선포하고 싶었던 것입니다. 이 패러다임을 객체지향프로그램(OOP, Object Oriented Programming)이라고 부릅니다. 객체지향 프로그래밍 이라는 패러다임은 '모든 것을 객체로 만드는' 프로그래밍 방식을 말합니다. 

이해를 돕기 위해 어떤 대상을 하나 떠올려봅시다. 아무 대상이나 떠올리면 됩니다. 마침 옆에 치약이 있네요. 치약은 이름, 회사, 성분 등의 속성을 갖고 있습니다. 또한 치약은 짜면 내용물이 나온다라는 기능이 있습니다. 치약을 짜는 힘과 짜는 시간에 따라 나오는 내용물의 양이 결정됩니다. 이는 치약의 '기능'입니다. 치약 뿐만 아니라 모든 대상은 속성과 기능으로 구성됩니다. 이와 같은 속성과 기능을 클래스로 구현할 수 있습니다. 세상 모든 것을 클래스로 만들 수 있는 것입니다. 

 

클래스 선언하기

클래스는 구조체와 선언방법이 같습니다. 치약을 예로 들어봅시다. 치약 클래스를 선언하겠습니다. 속성은 회사, 치약이름, 용량으로 설정하였습니다. 

class tp
{
    char company[10];
    char prod_name[10];
    float volume;
};


참고로 클래스 맴버로 대괄호 안을 비운 배열(ex. arr[ ] )을 선언하는 것은 불가능합니다. 구조체도 동일합니다.  

 

구조체와 달리 클래스는 맴버의 접근여부에 따라 맴버를 세가지로 구분하여 선언합니다.

private : 클래스 내부에서만 접근가능
public : 외부에서도 접근가능
protected : 상속관계인 경우 유도클래스에서 접근가능

이들을 접근제어 지시자라고 부릅니다. private 이 디폴트 값이기 때문에 위에서 선언한 tp 클래스의 모든 맴버는 private 이 되고, 외부에서 접근이 불가능해집니다. 아래와 같이 선언한 것과 같습니다 .

class tp
{
private:
    char company[20];
    char prod_name[20];
    float volume;
};

 

객체 생성하기

클래스를 생성했으니 이번에는 객체를 생성해봅시다. 클래스가 붕어빵 틀이라면 객체는 붕어빵입니다. pure 2080 치약의 객체 선언해봅시다. 일단은 구조체변수를 선언했던 것과 같은 방법으로 선언해봅시다. 

int main()
{
    tp pure2080={"LG","pure2080",150};
    
    return 0;
}


오류가 발생합니다. private 으로 분류된 클래스의 맴버값은 클래스 안에서만 접근할 수 있기 때문에 발생한 오류입니다. 위 코드를 보면 클래스 맴버에 접근해서 초기화를 시도하고 있습니다. 접근제어 지시자를 public 으로 바꿔주면 해결될 문제이지만, 함부로 클래스 맴버값에 접근하도록 하는 것은 좋지 않습니다. 객체 선언 시 맴버 값을 입력해주는 함수를 정의하는 편이 낫습니다. 아래와 같이 함수를 정의하면 됩니다. 아래 코드의 class 선언 안에 있는 SetValue 함수를 찾아보세요. 

class tp
{
private:
    char company[20];
    char prod_name[20];
    float volume;
public:
    void SetValue(const char * cp,const char* pr_n,const float vol); //이름만 선언. 

};

void tp::SetValue(const char * cp,const char* pr_n,const float vol) //내용은 밖에.
{
    strcpy(company,cp);
    strcpy(prod_name,pr_n);
    volume=vol;
}

 

SetValue 라는 함수를 선언했습니다. class 안에는 이름만 선언하고, 실제 내용은 밖에서 선언했습니다. 이는 가독성을 높이기 위함입니다. 함수의 인자를 보시면 배열을 입력받는 char *에 const 가 붙어 있습니다. C++에서는 char* 의 사용을 금하고 있기 때문에 const를 붙여서 사용해야 합니다.

 

맴버 중 문자열변수에 값을 입력하려면 strcpy 함수를 사용해야 합니다. strcpy 는 #include <cstring> 를 선언해야 사용할 수 있습니다. 위에서는 생략하였습니다. 

 

이제 객체를 생성해봅시다. 아래 코드와 같이 객체를 선언하고 나서, SetValue 함수를 이용하여 초기화를 해줍니다. 

 

int main()
{
    tp pure2080;
    pure2080.SetValue("LG","pure2080",150);
    
    return 0;
}

 

잘 입력되었는지 확인하고 싶은데 클래스 맴버에 접근할 수가 없어서 잘 입력된 것인지 확인할 수가 없습니다. 객체의 맴버값을 출력해주는 함수도 하나 정의합시다. PrintMember 함수를 찾으시면 됩니다. 

class tp
{
private:
    char company[20];
    char prod_name[20];
    float volume;
public:
    void SetValue(const char * cp,const char* pr_n,const float vol);
    void PrintMember(); //이름만 선언
};

void tp::SetValue(const char * cp,const char* pr_n,const float vol)
{
    strcpy(company,cp);
    strcpy(prod_name,pr_n);
    volume=vol;
}

void tp:: PrintMember() //내용은 여기
{
    cout<<"company : "<<company<<endl;
    cout<<"prod_name : "<<prod_name<<endl;
    cout<<"volume : "<<volume<<endl;
}

 

PrintMember 라는 함수를 추가했습니다. 맴버 값을 단순히 출력하는 함수입니다. 아래와 같이 사용합니다. 

int main()
{
    tp pure2080;
    pure2080.SetValue("LG","pure2080",150);
    
    pure2080.PrintMember(); //맴버 출력
    
    return 0;
}


//출력결과
//company : LG
//prod_name : pure2080
//volume : 150


오늘은 클래스가 등장한 맥락, 클래스의 선언 방법, 객체 생성방법, 객체 맴버 초기화 방법을 다뤘습니다. 

클래스를 잘 사용하기 위해 알아야 할 몇가지 기능들이 더 있습니다. 해당 내용들은 다음시간에 설명하겠습니다. 

 

#전체 코드

#include <iostream>
#include <cstring> //strcpy 사용

using namespace std;

class tp
{
private:
    char company[20];
    char prod_name[20];
    float volume;
public:
    void SetValue(const char * cp,const char* pr_n,const float vol);
    void PrintMember(); //이름만 선언
};

void tp::SetValue(const char * cp,const char* pr_n,const float vol)
{
    strcpy(company,cp);
    strcpy(prod_name,pr_n);
    volume=vol;
}

void tp:: PrintMember() //내용은 여기
{
    cout<<"company : "<<company<<endl;
    cout<<"prod_name : "<<prod_name<<endl;
    cout<<"volume : "<<volume<<endl;
}



int main()
{
    tp pure2080;
    pure2080.SetValue("LG","pure2080",150);
    
    pure2080.PrintMember(); //맴버 출력
    
    return 0;
}


//출력결과
//company : LG
//prod_name : pure2080
//volume : 150
반응형

댓글