C++은 절차 지향 프로그래밍(procedual oriented programming)과 다르게, 객체 지향 프로그래밍(object oriented programming)을 추구하는 프로그래밍 언어이다. 따라서 C++에서 객체의 개념을 이해하고 공부하는 것은 매우 중요하다. 이번 포스팅에서는 C++의 객체와, 객체를 생성할 수 있는 클래스에 대해서 공부해보자.
목차는 다음과 같다.
1. 객체
1.1 객체의 특징들
1.1.1 객체의 구성
1.2 객체 구성
1.3 C++ 클래스
2. C++ 클래스 작성하기
2.1 클래스 선언부
2.1.1 클래스 멤버
2.1.2 접근 지정자
2.2 클래스 구현부
3. 클래스 예제와 설명
객체(object)는 쉽게 말하면 세상 모든 것이다. OOP 언어들은 현실 세계의 사물인 개념을 프로그램 내에서 적절히 표현하기 위해서 객체라는 개념을 도입하였다. 객체는 상태와 행동을 가진 독립적인 개체로 취급되며, 프로그래밍 언어에서는 특정 클래스를 기반으로 생성된다.
객체는 속성(attribute)과 메서드(method)로 구성된다.
💡 속성
객체의 속성은 객체가 가지는 데이터나 상태를 의미한다. 객체는 클래스 내부에 변수로 정의되며, 이러한 변수들은 객체가 생성될 때 고유한 값을 가지게 된다. 상태(state)라고 용어를 사용할 때도 있다.
💡 메서드
객체가 할 수 있는 행동이나 기능을 정의한다. 클래스 내부에 함수로 정의되며, 객체가 특정 행동을 할 수 있도록 코드를 실행한다.
💡 캡슐화(encapsulation)
캡슐화는 객체의 구성 요소들을 캡슐로 싸서 보호하고, 외부에서 볼 수 없게 하는 것으로, 객체의 가장 기본적이고 본질적인 특징이다. 캡슐화를 통해서 외부 접근을 통제하고 객체 내부의 요소들을 방어한다. 단, 객체들이 서로 간의 데이터 공유 및 통신을 수행하기 위해선 일부 요소들은 공개하게 된다.
C++의 객체는 멤버 변수(member variable)과 멤버 함수(member function)로 구성된다. 멤버 변수들은 객체의 상태를 나타내는 속성들이고, 멤버 함수는 객체의 동작과 행동을 구현한 메서드라고 할 수 있다.
C++ 클래스는 객체를 정의하는 틀, 설계도, 청사진 정도로 정의할 수 있다. 클래스에서 멤버 변수와 멤버 함수를 선언하게 된다. C++ 객체(object)는 클래스(class)에서 찍어내며, 멤버 변수와 멤버 함수를 가지고 실제 C++ 프로그램 내부에서 작동하는 인스턴스(instance)가 된다고 받아들이면 될 것 같다. 보통, 독립적으로 존재하는 개체를 객체라고 부르며, 클래스로부터 생성된다는 의미로 작성할 때는 인스턴스라는 표현을 더 많이 사용한다.
클래스 하나를 통해서 여러 객체를 생성할 수 있으며, 멤버 변수와 멤버 함수의 값을 달리 줌으로써 객체들을 모두 다르게 생성할 수 있다.
C++에서는 class라는 키워드를 통해서 클래스를 선언한다. C언어의 구조체(struct)와 유사한 자료형으로, C++ 클래스를 작성하는 것은 공부해보도록 하자.
클래스는 일반적으로 클래스를 선언하는 부분(calss declaration)과 클래스 구현부(class implementation)으로 나누어서 작성한다. 선언부에서 class 키워드를 통해서 클래스를 선언하고, 구현부에서 멤버 변수들과 멤버 함수들을 통해 겍체를 구현할 수 있는 기능을 제공한다.
클래스 선언부는 class 키워드와 클래스의 이름으로 구성된다.
class Circle{
...
};
키워드와 이름만 작성하면 되는거니까 그렇게 어렵지 않다. 마지막에 세미콜론을 붙여야 한다는 것만 유념하자. 요즘 IDE들은 잘 되어있어서 세미콜론 안붙히면 칼같이 뭐라 하더라..
클래스 멤버는 변수와 함수로 구성되는데, C++11부터 멤버 변수는 클래스 선언부 내부에서 초기화 할 수 있도록 설정되었다.
class Circle{
public:
int radius = 10;
...
};
멤버 함수는 선언부에서 원형(prototype) 형태로만 선언되며, 리턴 타입과 매개변수 리스트 등이 모두 선언되어야만 한다.
C++의 접근 지정자는 외부에서 클래스 내부로 접근하는 것을 통제하기 위해서 도입된 개념이다. 접근 지저아가 선언되면, 다른 접근 지정자로 선언될 때까지 모든 멤버에 대해서 적용된다.
C++의 접근 지정자는 public, private, protected 이렇게 3가지 종류가 있다.
class Circle {
public:
...
};
클래스 구현부에서는 선언부에 선언된 멤버 함수들의 코드를 구현하게 된다.
구현할 때 주의해야 하는 것은 C++의 범위 지정 연산자 ::를 사용해서 다음과 같은 형식으로 구현해야 한다는 것이다.
<함수 리턴 타입> <class 이름>::<함수 이름>(매개변수 리스트){
- 구현 -
}
이는 다른 클래스 내부에 같은 이름의 멤버 함수가 존재할 수 있기 때문이다.
참고로, 클래스 선언과 구현을 분리하는 것은 클래스를 재사용하기 위함이다. 클래스를 사용하고자 하는 다른 C++ 파일들을 컴파일 시 클래스의 선언 부분만 가져오면 된다.
#include <iostream>
using namespace std;
class Circle{
public:
int radius;
double getArea();
};
// class 선언부
double Circle::getArea(){
return 3.14 * radius * radius;
}
// class 구현부
int main(){
Circle donut;
double.radius = 1;
double area = donut.getArea();
cout<<area<<endl;
Circle pizza;
pizza.radius = 10;
area = pizza.getArea();
cout<<area<<endl;
}
마치 C++에서 변수 이름을 통해 변수를 생성하고 그에 맞는 메모리 공간을 할당하는 것처럼, 클래스도 클래스 이름을 통해 생성하고 그에 맞는 메모리 공간을 할당한다. 이때 겍체의 메모리 할당은 멤버 변수의 메모리와 멤버 함수의 메모리를 모두 포함한다.
그리고 특정한 이름을 통해서 클래스를 선언하고 생성하게 되면, 객체 멤버 변수 및 함수를 "."을 통해서 자유롭게 사용할 수 있다. radius나 getArea()의 값을 변경하고, 사용한 것이 그 예시이다.
또 다른 예시 코드를 한번 보자.
#include <iostream>
using namespace std;
class Rectangle {
public:
int width;
int height;
int getArea();
};
int Rectangle::getArea() {
return width * height;
}
int main() {
Rectangle rect;
rect.width = 3;
rect.height = 5;
cout << rect.getArea() << endl;
}