Nuke Olaf - Log Store

[C++] 클래스와 객체란 무엇인가? 본문

Language/[C++]

[C++] 클래스와 객체란 무엇인가?

NukeOlaf 2020. 10. 12. 00:29

기말고사 대비 겸 수업에서 배운 클래스와 객체에 대해 정리해보았다.

0. 객체지향 프로그래밍

클래스와 객체에 대해 이야기 하기 전에, 먼저 객체 지향에 대해 간단하게 생각해 보자.

C++ 는 객체지향 언어이다. 그래서 C++ 언어를 사용하면 객체지향 프로그래밍을 할 수 있다. 물론, 객체지향 언어를 이용해 절차지향 프로그램을 만들거나 다른 프로그래밍 패러다임을 사용한 프로그램을 만들 수 있다. C++ 언어 자체는 객체를 만들 수 있는, 객체지향 프로그래밍에 적합하게 설계된 언어라는 것이 중요하다.

객체지향 프로그래밍(Object-Oriented Programming)에서는 클래스와 객체가 핵심이며, 매우 중요한 개념이다. 객체지향 프로그래밍은 클래스를 통해 객체를 만들고, 만들어진 객체를 사용하는 프로그램을 만드는 것이기 때문이다. 그렇기 때문에 객체지향 프로그래밍을 하기 위해서는 클래스와 객체가 무엇이며, 왜 사용하는지, 어떻게 사용하는지 알고 있어야 한다.

 

1. 클래스(class)란?

 클래스는 추상 자료형(ADT, Abstract Data Type)을 표현하기 위해 사용되는 표현 방법이다. 지금까지 우리가 공부했던 자료형(Data Type)인 int, char, boolean 과 같은 것들은 원시 자료형(Primitive Data Type) 또는 기본 자료형이라고 한다. 그런데, 만약 원시 자료형보다 더 복잡한 개념을 가진 변수가 필요하다면 어떻게 해야할까?

내가 멋진 게임 개발자가 되어 카트라이더라는 레이싱 게임을 만드는 상황을 예로 들어보겠다. 카트라이더에는 플레이어가 탑승하여 경주하는 자동차인 "카트"가 있다. 카트마다 이름도 다르고, 속도도, 색상도 모두 제각각이다. 하지만, 모든 카트에는 "이름"과 "속도"와 "색상"이라는 속성이 존재한다. 매번 카트를 만들 때마다 이름과 속도와 속성을 작성하는 것보다, 이 공통된 속성을 하나로 모아 하나의 자료형으로 만들면 편하지 않을까?

이런 생각을 코드로 구현해보면 다음과 같다. 나는 Cart 라는 내가 정의한 자료형을 사용하여 cart1, cart2, cart3 라는 변수들을 만들어냈다. 여기서 Cart 라는 이름의 자료형이 바로 클래스이고, cart1, cart2, cart3 이라는 이름으로 초기화된 변수들이 바로 객체이다.

클래스는 이렇게 사용자가 정의한 자료형이기 때문에 기본 타입과 구별하기 위해 사용자 정의형(user defined type)이라고도 부른다. 사실 클래스 뿐만 아니라 C언어에서 공부한 구조체, 공용체, typedef 도 모두 새로운 타입을 만드는 것이므로 이것들도 모두 사용자 정의형이다. 사용자 정의형은 int, char, boolean 과 같은 기본 타입과 마찬가지로 새로운 타입일 뿐, 메모리에 저장된 변수가 아니다. 그렇기 때문에 int a; 로 타입을 통해 변수를 선언하는 것처럼 클래스를 통해 객체를 선언하고 초기화해주어야만 객체를 사용할 수 있다.

 

2. 클래스를 사용하는 방법

클래스는 속성(attribute)과 메서드(method)로 이루어져 있다. 위의 카트라이더 예시에서 Cart 클래스를 만드는 경우를 아래와 같이 생각해볼 수 있다.

클래스의 속성은 name, color, speed 이고, 클래스의 메서드는 start, stop, drift 이다. 위와 같이 클래스의 속성과 메서드는 변수와 함수로 표현된다. 속성은 어떤 값을 저장하는 역할을 하기 때문에 변수로 표현되고, 메서드는 기능적인 측면을 표현하는 것이므로 함수로 표현되는 것이다. 클래스 내의 변수와 함수들은 멤버 변수, 멤버 함수라고 부른다.

(1) 클래스의 객체를 생성했을 때의 메모리 구조

클래스의 객체를 생성하면, 객체의 멤버 변수들은 개별적으로 메모리에 생성된다. 그러나, 클래스의 멤버 함수는 객체의 개수에 관계 없이 단 하나만 생성된다. 멤버 함수를 호출할 때마다 내부적으로 멤버 함수를 호출하는 객체의 주소값을 넘겨 사용하기 때문에 멤버 함수는 어떤 객체의 멤버 변수를 변경해야할 지 알 수 있다. 그래서 클래스의 멤버 함수는 객체의 개수에 상관 없이 단 하나만 생성된다.

(2) public 과 private

객체지향 프로그래밍은 데이터 캡슐화(encapsulation) 라는 특징을 지닌다. 캡슐화는 객체의 속성과 행위를 하나로 묶고, 객체가 가진 데이터를 외부로부터 감춰 은닉하는 것이다. 데이터를 안전하게 보호하는, 정보 은닉(information hiding)을 위해 클래스 내의 멤버를 외부로부터 감추는 것은 캡슐화의 중요한 특징이다.

하지만 객체가 어떤 기능을 수행하기 위해서 특정한 데이터를 외부에 노출시켜야 하는 경우가 있다. 그래서 클래스는 자신의 멤버들을 보호할 대상과 외부로 공개할 대상으로 나누어 설정할 수 있어야 한다. C++ 에서는 외부에 공개할 멤버는 public 으로, 외부로부터 접근을 차단할 멤버는 private 으로 구분한다.

만약 클래스 내의 멤버들에 대해 private 과 public 을 명시하지 않는다면, 모두 private 으로 간주된다.

class Person {
    private :
        string name;
        int height;
        int weight;
    
    public : 
        void Print() { 
        	cout << name << " is " << height << "cm tall and weighs " 
            	<< weight << "kg." << endl; 
        }
        void Change(int a, int b) { height = a; weight = b; }
};

위의 예제의 Person 클래스는 멤버 변수들이 모두 private 으로 설정되어있고, 멤버 함수들은 모두 public 으로 설정되어 있다. 이제 외부에서는 Person 클래스의 객체의 멤버변수에 접근할 수 없지만, Print() 함수나 Change() 함수에는 접근할 수 있다.

(3) 구조체(struct)와 클래스(class)

구조체는 클래스와 같은 사용자 정의형(user defined type)이다. C에서의 구조체는 private 이나 public 과 같은 개념이 없이 무조건 외부 접근을 허용한다. 그러나, C++에서의 구조체는 C에서의 구조체의 개념이 확장되어 구조체에서도 private과 public을 지정하고, 구조체를 클래스의 개념과 거의 동일하게 사용할 수 있다. 

다만, 클래스와 달리 구조체에서는 private 과 public 을 명시하지 않는다면 구조체 내의 멤버들이 private 으로 간주된다.

class Person {
	string name;   
    private :
        int height;
        int weight;
    
    public : 
        void Print() { 
        	cout << name << " is " << height << "cm tall and weighs " 
            	<< weight << "kg." << endl; 
        }
        void Change(int a, int b) { height = a; weight = b; }
};

struct Person {
 	string name;
    private :
        int height;
        int weight;
    
    public : 
        void Print() { 
        	cout << name << " is " << height << "cm tall and weighs " 
            	<< weight << "kg." << endl; 
        }
        void Change(int a, int b) { height = a; weight = b; }
};

위의 예제에서 클래스 Person 과 구조체 Person 은 동일하게 사용될 수 있다. 다만, 클래스의 name 변수의 경우 private 으로 간주되어 외부에서 접근할 수 없으며, 구조체의 name 변수의 경우 public 으로 간주되어 외부에서 접근하여 값을 수정할 수 있다.

(4) 객체의 생성과 생성자(constructor), 객체의 소멸과 소멸자(destructor)

생성자(constructor) 란, 클래스 객체를 생성할 때, 객체의 멤버 변수를 초기화하는 함수이다. 생성자는 객체 생성시 반드시 한 번만 호출된다. 생성자는 일종의 멤버 함수이다. 그래서 함수와 비슷한 특징을 가지며, 생성자 오버로딩을 통해 다양한 생성자를 만들 수 있다. 생성자의 이름은 클래스의 이름과 같다.

소멸자(destructor) 란, 객체가 소멸될 때 호출되는 함수이다. 생성자와 마찬가지로 객체가 사라질 때 반드시 한 번만 호출된다. 객체가 소멸되는 시점은 일반 변수의 생명주기에서의 변수 소멸시점과 동일하다. 객체가 지역변수일 경우에는 해당 지역(함수 또는 블록)의 수행이 완료되고 반환될때 소멸된다. 객체가 전역변수일 경우에는 프로그램이 종료될 때 소멸된다. 소멸자의 이름은 클래스의 이름과 동일하나, 생성자와의 구별을 위해 이름 앞에 '~' 문자가 붙는 것이 특징이다.

소멸자는 동적할당 메모리를 해제하거나, 임시 메모리를 지우기 위해 주로 사용한다.

class CPoint {
    private :
        int x;
        int y;
    
    public :
        CPoint(int a, int b) { x = a; y = b; cout << "constructor1:"; Print(); }
        CPoint(int a) { x = a; y = 0; cout << "constructor2:"; Print(); }
        ~CPoint() { cout << "destructor:"; Print(); }
        void Print() { cout << "(" << x << ", " << y << ")" << endl; }
};

 

(5) 생성자와 소멸자의 호출 순서

생성자의 경우 전역 객체 생성자 -> 지역 객체 생성자 순으로 호출된다.

소멸자의 경우 생성자의 호출 순서의 역순으로 수행된다. 객체가 스택(LIFO)에 쌓이는 방식으로 동작하기 때문이다.

 

3. 클래스 사용 예시

#include <iostream>
using namespace std;

class Person {
    private :
        string name;
        int height;
        int weight;
    
    public : 
        Person(string a, int b, int c) { name = a; height = b; weight = c; }
        ~Person() { cout << name << " died." << endl; }
        void Print() {  
        	cout << name << " is " << height << "cm tall and weighs " 
        	<< weight << "kg." << endl; 
        }
        void Change(int a, int b) { height = a; weight = b; }
};

int main(void) {
    Person P1("Kim Byung-Gi", 180, 70);
    Person P2("Park Hye-Young", 160, 50);

    P1.Print();
    P2.Print();
    P1.Change(185, 75);
    P1.Print();
    
    return 0;
}

출력 결과

Kim Byung-Gi is 180cm tall and weighs 70kg.
Park Hye-Young is 160cm tall and weighs 50kg.
Kim Byung-Gi is 185cm tall and weighs 75kg.
Park Hye-Young died.
Kim Byung-Gi died.

 

>>>참고 : 기초를 탄탄하게 세워주는 C++ 프로그래밍 입문 (황준하, 김성영 지음)

 

Comments