C++/C++ 기초

[C++] 클래스 ( Class )

Song 컴퓨터공학 2023. 5. 4. 21:55

클래스란 C++ 에서 지원하는 문법으로, 구조체의 상위 호환으로 생각할 수 있습니다. 구조체는 서로 다른 타입의 변수들을 묶어서 하나의 자료형으로 정의할 수 있었습니다. 클래스는 멤버로 함수를 포함할 수 있기 때문에 C 언어의 구조체보다 더 확장된 의미를 가집니다.

 

클래스의 멤버 변수를 property, 멤버 함수를 메소드(method) 라고 합니다.

 

C++ 에서는 이러한 클래스를 가지고 객체 지향 프로그램을 작성할 수 있습니다.

 

객체 지향 프로그래밍(OOP, Object-Oriented Programming)

 

C++ 은 객체 지향 프로그래밍 언어다. 이런 말을 모두 들어보고 C++ 공부를 시작하셨겠죠? 객체 지향 프로그래밍에서는 모든 데이터를 객체(object)로 취급하며, 객체가 프로그래밍의 중심이 됩니다.

 

C++ 나 Java 같은 객체 지향 언어는 중요한 3가지 특성을 가집니다.

Encapsulation(캡슐화), Inheritance(상속), Polymorphism(다형성) 이라는 대표적인 3가지 특성을 가지는데, 이 중에서 Encapsulation 을 가질 수 있도록 해주는 것이 바로 C++에서의 클래스 입니다.

 

객체란 사전적인 정의로 실제 존재하는 것을 말합니다. 객체 지향 이론에서는 사물 처럼 유형적인 것 뿐 아니라 개념이나 논리와 같은 무형적인 것들도 객체로 간주합니다. 객채의 상태(state)와 행동(behavior) 을 구체화 하는 형태의 프로그래밍이 OOP 입니다. 이와 같은 객체를 만들어내기 위한 틀이 바로 클래스(class) 입니다.

 

그런데 객체라는 개념이 너무 추상적이고 모호하지 않나요? 어렵게 생각하지 말고 프로그래밍에서의 객체는 클래스에 정의된 내용대로 메모리에 생성된 것 이라고 배우셔도 무방합니다.

 

 

인스턴스 ( instance )

C++ 에서 클래스는 구조체와 마찬가지로 사용자가 정의하는 일종의 타입입니다. 따라서 클래스를 사용하기 위해서는 해당 클래스 타입의 객체를 선언해야 합니다. 이렇게 선언된 해당 클래스 타입의 객체인스턴스(instance) 라고 하며, 메모리에 대입된 객체를 의미합니다.

 

하나의 클래스에서 여러 개의 인스턴스를 생성할 수 있습니다. 인스턴스는 독립된 메모리 공간에 저장된 자신만의 멤버 변수를 가지지만, 멤버 함수는 모든 인스턴스가 공유합니다.

 


class circle
{
private: // private 제어 지시자는 생략 가능
	int xpos;
        int ypos;
        int radius;
public:
	int distance;
        ...
};

circle 이라는 이름의 클래스를 정의한 예시입니다. 구조체와 비슷한 방식으로 struct 키워드 대신 class 키워드를 사용하고, 접근 제어 지시자를 함께 사용해 클래스를 선언합니다. 접근 제어 지시자는 아래에 설명이 나와 있습니다.

 

이렇게 선언된 클래스의 정의를 가지고 circle 객체를 선언할 수 있습니다.

circle C1;
// 클래스이름 객체참조변수이름;

클래스는 결국 구조체 + 멤버 함수를 정의할 수 있다. 라고 생각하셔도 무방합니다. 클래스에서 멤버 함수를 정의하는 방법은 일반 함수와 비슷합니다. 멤버 함수는 꼭 클래스의 선언 안에서 해야 하는 것이 아니고, 원형만 참조해도 되며 안이나 밖 모두에서 정의할 수 있습니다.

 

대신 클래스의 선언 밖에서 멤버 함수를 정의할 때는 범위 지정 연산자(::)를 사용하여 해당 함수가 어떤 클래스에 속하는지를 명시해야 합니다.

 

void circle::Shifting(int x, int y) { xpos += x; ypos += y; }

만약 멤버 함수가 클래스 선언 내부에서 정의되면, 이 함수는 인라인 함수로 처리되기 때문에 위처럼 범위 지정 연산자를 사용하여 소속 클래스를 명시할 필요가 없습니다. 또한 클래스 선언 밖에서 정의된 함수도 inline 키워드를 사용하면 인라인 함수로 처리 가능합니다.

 

그보다 더 중요하고 기억해야 할 것은, 멤버 함수는 클래스의 선언 안이나 밖에서 모두 정의할 수 있지만 클래스가 일단 선언된 후에 멤버 함수를 추가할 수는 없습니다. 이처럼 하나의 클래스에서 생성된 인스턴스는 각각 독립된 메모리 공간에 저장된 자신만의 멤버 변수를 가지지만 멤버 함수는 모든 인스턴스가 공유하게 됩니다.

 


멤버 함수의 호출

C++ 의 클래스에서 멤버 함수를 호출하는 방법은 구조체와 동일합니다. 멤버 함수는 멤버 참조 연산자(.) 를 사용하여 호출할 수 있습니다.

객체이름.멤버함수이름();		// 매개변수가 없는 멤버 함수 호출
객체이름.멤버함수이름(전달인수);	// 매개변수가 있는 멤버 함수 호출

접근 제어(access control)

 

C++ 에서 구조체의 모든 멤버는 외부에서 언제나 접근할 수 있습니다. 하지만 클래스는 OOP의 기본 규칙 중 하나인 정보 은닉(data hiding) 도 필요합니다. 정보 은닉이란 사용자가 굳이 알 필요 없는 정보는 숨긴다는 개념인데, 이를 통해 사용자는 최소한의 정보만으로 프로그램을 손쉽게 사용할 수 있게 됩니다.

 

이러한 정보 은닉을 위해 제공하는 기능이 접근 제어(access control) 입니다. 접근 제어란 접근 제어 지시자를 사용해 클래스 외부에서의 직접적인 접근을 허용하지 않는 멤버를 설정할 수 있게 하는 기능입니다.

 

C++은 3 가지의 접근 제어 지시자가 있습니다.

 

1. public ( 공용, 어디서든 접근 가능)

2. private ( 같은 클래스 내에서만 접근 가능 )

3. protected ( 같은 패키지, 다른 패키지의 파생 클래스에서만 접근 가능)

 

구체적인 내용은 아래에서 알아보겠습니다. 클래스의 기본 접근 제어는 private 이며, 구조체 및 공용체는 public 입니다.

 

public 접근 제어 지시자

 

public 접근 제어 지시자를 사용하여 선언된 클래스 멤버는 외부로 공개되고, 해당 객체를 사용하는 프로그램 어디서든 직접 접근할 수 있습니다. public 멤버 함수는 해당 객체의 private 멤버와 프로그램 사이의 인터페이스(interface) 역할을 합니다. 프로그램은 이런 public 멤버 함수를 통해 해당 객체의 private 멤버에도 접근할 수 있습니다.

 

 

private 접근 제어 지시자

private 접근 제어 지시자를 사용해 선언된 클래스 멤버는 외부에 공개되지 않으며, 직접 접근도 불가합니다. 프로그램은 private 멤버에 직접 접근할 수 없으며, 해당 객체의 public 멤버 함수를 통해서만 접근할 수 있습니다. 클래스의 기본 접근 제어 권한은 private 로 설정되어 있기 때문에 만약 private 로 클래스를 사용할 시 private 접근 제어 지시자는 생략할 수 있습니다. 일반적으로 private 멤버는 인터페이스를 직접 구성하지 않는 클래스의 세부적인 동작을 구성하는데 사용됩니다.

 

protected 접근 제어 지시자

C++ 클래스는 기본적으로 private 멤버로 정보를 은닉하고, public 멤버로 사용자나 프로그램 인터페이스를 구축합니다. protected 는 파생 클래스(derived class) 와 관련된 접근 제어 지시자입니다. protected 멤버는 파생 클래스에 대해서는 public 멤버처럼 취급되며, 외부에서는 private 멤버처럼 취급됩니다.

 

protected 멤버에 접근할 수 있는 영역은

 

1. 이 멤버를 선언한 클래스의 멤버 함수

2. 이 멤버를 선언한 클래스의 프랜드

3. 이 멤버를 선언한 클래스에서 public 또는 protected 접근 제어로 파생된 클래스


몇 가지 예제 코드를 통해 class 를 선언하고 구현하여 사용하는 예시 코드를 보며 마치도록 하겠습니다.

// Rectangle 클래스 만들기
#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;
}

가장 쉬운 예시로 Rectangle class 를 구현해보았습니다. 우선은 구현을 보기 위해 모두 public 지시자를 통해 선언하고, 밑에 getArea() 함수를 구현하는 부분과 main 함수 내에서 객체 rect 를 생성하고 멤버변수나 멤버함수에 접근하는 방법도 모두 들어있는 코드입니다.

 

// Car class
#include <iostream>
#include <string>
using namespace std;

class Car {
private:
	string colar;
	int speed;
public:
	//void setColor(string c) { colar = c;}
	//void setSpeed(int s) { speed = s; }
	void setColor(string colar) { this->colar = colar; }
	void setSpeed(int speed) { this->speed = speed; }
	void speedUp() { speed += 10; }
	void speedDown() { speed -= 10; }
	void printCar() { cout << "현재 차의 색상은 " << colar << "\t" << "현재 차의 속도는 " << speed << endl; }
};

Car globalCar;

int main() {
	Car localCar;
	globalCar.setColor("white"); globalCar.setSpeed(100);
	globalCar.speedUp(); globalCar.printCar();

	localCar.setColor("red"); localCar.setSpeed(200);
	localCar.speedDown(); localCar.printCar();
	return 0;
}

이번에는 car class 를 정의하고 사용합니다. 여기에서 아직 배우지 않은 this 포인터를 사용하는데요, this 포인터를 사용하는 부분을 주석처리하고 바로 위 2줄의 주석을 해제해도 동일한 코드로 동작합니다. this 포인터는 자기 자신을 가리키는 포인터인데 자세한 내용은 따로 다뤄보도록 하겠습니다. Rectangle 과 다른 점은 멤버 변수는 private 로 선언하고, public 으로 선언한 멤버 함수를 통해 멤버 변수에 접근하는 모습을 볼 수 있습니다. 이런 경우에는 생성한 객체 localCar 이나 globalCar 에서 . 을 통해 멤버 변수에 접근할 수 없고, 멤버 함수를 통해 간접적으로만 접근할 수 있습니다.

 

 

 

 

이미지 출처 및 참고 문헌 :

http://www.tcpschool.com/cpp/cpp_class_accessControl

명품 C++ Programming