이미 객체 지향 프로그래밍과 클래스의 관계는 앞서 설명했다. 그러니 클래스의 특징 네가지를 설명하겠다
클래스의 특징 네가지는 1.은닉화, 캡슐화 ,상속성, 다형성이다. 이중 앞의 두개인 은닉화와 캡슐화만을 설명할것이다. 왜냐고?? 아직 안배움ㅋㅋ
그리고 이 특징들은 모두 "코딩 철학" 이지 규칙이 아니라 지키지 않는다고 해도 컴파일러가 찾아오진 않는다. 다만 같이 일하는 동료가 찾아온다. 둘중 뭐가 더 무서울까 고민해보자.
1. 은닉화
은닉화는 말 그대로 구성요소를 보여주지 않겠다는 의미이다. 그래서 기본적으로 파일을 헤더파일과 소스파일로 나누게 된다.
선생님은 이것을 식당의 메뉴판과 레시피에 비유하였다.
식당에 가면 모든 손님은 메뉴판을 볼 수 있다. 하지만 그 메뉴의 레시피를 볼 수는 없게 된다. 이유는 간단하게 레시피를 따라하는 소위 표절의 문제가 생길 수 있기 때문이다.
즉 이제 클래스의 선언은 헤더파일에 하고, 그 헤더파일을 참조하는 소스파일을 만들어 그 파일 내에 있는 함수들을 정의하는것이다.
또한 구조체와 달리 멤버 변수들을 외부에서 접근하지 못하는 private모드로 적고 그에대해 조작하는것은 클래스 내부 함수를 통해 조작하는것 또한 은닉화의 일환이다.
즉 정해진 루트만을 통해 접근할 수 있게 막아두는것이다.
즉 헤더에는 메뉴 이름만을 적고, 소스파일에는 모든 레시피를 적는것이다.
2. 캡슐화
캡슐화는 따로 구분을 하기 위한 요소이다. 다른 클래스라면 그 클래스에 해당하는 함수들만을 넣어 클래스에 문제가 생겼을때에는 그 클래스가 정의된 파일을 찾아가면 되는것이다. 물론 각 클래스끼리 파일을 분리하는것 또한 캡슐화라고 할 수 있다.
즉 캡슐화는 끼리끼리 모아두기 라고 보면 된다.
플레이어의 데이터를 조작하는것은 플레이어클래스에, 몬스터의 데이터를 조작하는것은 몬스터 클래스에 적는것이다.
하지만 이럴경우 문제가 생기게 된다. 더 무서운건 이 문제에 정답은 없다는것이다.
만일 몬스터와 플레이어가 싸우는 두개의 데이터가 모두 조작되는 함수는 몬스터에 넣어야할까 플레이어에 넣어야할까? 아니면 별개의 또다른 클래스를 만들어야할까?
이 두가지는 클래스의 특징이자 클래스를 사용하는 관습적 규칙이다. 지향해야한다는것이지, 어떤 코드가 은닉화를 지키지 못했다고 잘못된 코드이거나 하지 않는다.
이제부터는 클래스 선언방식과 사용방식을 적어보겠다. 당연히 은닉화와 캡슐화를 지켜 두개의 파일로 나누어 적겠다.
클래스.h(헤더 파일)
class 클래스명{
private :
멤버변수1,
멤버변수2...
public :
반환타입 멤버함수1(매개변수),
반환타입 멤버함수2(매개변수),
}
클래스 .cpp(소스 파일)
#prigma once
#include "클래스.h"
반환타입 클래스 :: 멤버함수1(매개변수){
멤버함수 내용
}
특별할건 없다. 구조체와 동일하게 선언하되 선언명이 class인거 뿐이다.
다만 구조체와의 차이점은 구조체는 기본값이 public 인 반면 클래스는 기본값이 private으로 조금더 은닉화를 꾀했다고 할 수 있다.
그러다보니 사실 저렇게 클래스 정의 처음을 private로 할 필요는 없으나. 만일을 위해 적어두는게 좋을것같다.
그리고 멤버함수의 정의는 cpp파일 내에서 클래스명 :: 을 통해 어떤 클래스 내의 함수인지 알려준 후 함수를 정의하는것과 동일하게 진행하면 된다.
물론 정의를 위해서는 클래스 헤더파일을 include 해줘야한다.
함수를 그냥 헤더 파일에서 정의해도 되지만 이는 아까 말한 은닉화에 어긋나는 방침이다. 또한 이 클래스에 있는 함수에 매개변수를 받아 다른 클래스의 정보를 조작하는거 또한 불가능하진 않지만 이 클래스 내의 멤버변수만을 조작하는것이 캡슐화를 지키는 방법일것이다.
생성자와 소멸자
생성자와 소멸자를 설명하기 전에 객체를 생성하는 과정을 먼저 알아보자. 이미 클래스가 정의되었다고 가정하고, 그 객체를 선언할 경우 컴퓨터는 다음과 같은 과정을 거치게 된다.
1. 필요한 크기만큼 메모리에 공간을 할당한다.
2. 그 객체의 생성자를 호출한다.
모든 객체에는 생성자와 소멸자가 있다. 굳이 클래스 말고 객체라고 표현한 것은 구조체 또한 객체이고, 이또한 생성자와 소멸자가 있기 때문이다.
모든 생성자와 소멸자는 반환타입 없이 객체명() 이 생성자이고 ~객체명() 이 소멸자이다.
생성자
클래스명();
소멸자
~클래스명();
또한 특별히 지정하지 않는다면 디폴트 생성자와 디폴트 소멸자가 자동으로 인식되어 작동하게 된다. 디폴트 생성, 소멸자는 특별히 무슨 행동을 하는것 같지는 않으나 가장 중요한 기능인 클래스의 멤버함수와 멤버변수에 접근할 수 있는 권한을 부여하게 된다. 즉 생성자가 소환되지 않으면 멤버함수나 멤버변수에 접근할 수 없다는 뜻이다.
그리고 직접적으로 클래스 내부에 저렇게 생성자를 선언하고 멤버변수의 초기화또한 가능하다.
물론 멤버함수나 멤버변수에 접근하게 하는 기능은 자동으로 진행되고, 이를 막을 방도는 없다.
또한 생성자에 매개변수를 넣을 수 있다. 즉 매개변수를 통한 초기화가 가능한것이다. 이또한 함수 오버로딩처럼 매개변수의 타입과 갯수에 따라 여러가지 생성자를 만들 수 있게 되는데, 만일 매개변수가 있는 생성자를 하나 이상 만들고, 매개변수가 없는 생성자는 선언하지 않았다면 디폴트 생성자는 호출되지 않으므로 선언 즉시 매개변수를 입력해 주어야한다.
물론 그것보다 좋은 방법은 매개변수 없는 생성자를 만드는것이다.
생성자를 호출하는 법은 다음과 같다.
1. 클래스 변수 선언하기
즉
클래스명 클래스변수;
클래스명 클래스변수(매개변수);
라고 선언하면 자동으로 생성자가 호출된다.
2. 동적 할당하기
클래스명* 클래스포인터변수명 = new 클래스명;
클래스명* 클래스포인터변수명 = new 클래스명(매개변수);
이렇게 new를 통한 클래스 동적할당의 경우 할당되는 즉시 생성자가 호출된다.
하지만 똑같은 동적할당이지만 malloc의 경우 생성자를 호출하는 기능이 없으므로, 멤버변수와 함수에 접근하지 못하게 된다. 그러니 객체를 동적할당할때에는 무조건 new를 사용하자.
이번에는 소멸자이다.
객체의 소멸의 경우에는 다음과 같은 과정을 거친다
1. 소멸자가 호출된다
2. 할당공간을 반환한다.
놀랄거 있는가 결합은 분해의 역순이다.
소멸자의 경우에는 매개변수를 받을 수 없다. 아니 곧 죽어가는 마당에 매개변수 받아서 뭐하려고
이 소멸자의 경우 이후 코드가 없는 경우 즉 프로그램이 종료되는 시점에 자동으로 소환된다. 그래서 보통 동적할당한 메모리를 반납하여 메모리 누수를 방지하는 목적으로 쓰일 수 있다.
그리고 만약 클래스1 안에 클래스2가 있고 클래스 1이 소멸하는 시점에 자연히 클래스 2의 소멸자 먼저 호출되어 정상적으로 객체가 사라진 후 클래스 1의 소멸자가 호출되니 그냥 소멸자마다 반납명령을 적어놓으면 도미노처럼 연달아 호출되며 반납되니 참으로 편리한 일이다.
클래스 생성과 소멸에 대한 예시는 다음에 다루겠다.
이상 클래스 기본지식을 마친다.
'c언어' 카테고리의 다른 글
상호참조와 전방선언 (0) | 2023.02.13 |
---|---|
클래스 사용 예시 (0) | 2023.02.10 |
2개월차의 시작, 객체 지향 프로그래밍? (0) | 2023.02.10 |
매크로와 레퍼런스 (0) | 2023.02.09 |
파일 입출력 -파일 읽기 및 쓰기 (0) | 2023.02.09 |