*윤성우의 열혈 C++ 프로그래밍 교재로 학습하며 정리하였습니다.
좋은 클래스가 되기 위한 최소한의 조건으로는
정보은닉
캡슐화
이 두가지가 있다.
정보은닉의 이해
정보 은닉이란 안정성을 높이기 위해 클래스 내의 멤버 변수들(정보)을
외부에서 접근하지 못하도록 private로 선언하는 것을 말한다.
그림판처럼 다양한 도형을 그릴 수 있는 프로그램을 구현한다고 가정해보자.
#include <iostream>
using namespace std;
class Point {
public://public으로 선언되어 어디서든 접근이 가능하다.
int x;//x좌표의 범위는 0이상 100이하
int y;//y좌표의 범위는 0이상 100이하
};
class Rectangle {//직사각형을 표현
public:
//클래스의 멤버로 객체를 둘 수 있다.
Point upLeft;
Point lowRight;
public:
void ShowRecInfo() {
cout << "좌 상단: " << '[' << upLeft.x << ", ";
cout << upLeft.y << ']' << endl;
cout << "우 하단: " << '[' << lowRight.x << ", ";
cout << lowRight.y << ']' << endl << endl;
}
};
int main(void) {
//직사각형을 표현할 두 개의 Point 객체를 생성
//멤버변수가 public으로 선언되면, 구조체 변수를 초기하듯이 초기화가 가능하다.
Point pos1 = { -2, 4 };
Point pos2 = { 5, 9 };
//위에서 생성한 Point객체를 이용하여 Rectangle 객체를 생성 및 초기화
Rectangle rec = { pos2, pos1 };
rec.ShowRecInfo();
return 0;
}
위 코드는 Point클래스의 멤버변수가 public으로 선언되었을 때 발생할 수 있는 문제점을 보이고 있다.
Rectangle rec = {pos2, pos1};
이 문장에 의해서 생성되는 객체는 메모리 상에서 아래의 형태로 존재하며,
생성된 두 개의 Point 객체에 저장된 값이 Rectangle 객체의 멤버에 대입이 된다.
위 예제 코드에는 다음과 같은 문제점이 있다.
1. 점의 좌표는 0이상 100이하가 되어야 하는데, 그렇지 못한 Point 객체가 존재
2. 직사각형을 의미하는 Rectangle 객체와 좌 상단 좌표 값이 우 하단 좌표 값보다 크다.
Point.h
#ifdef __POINT_H_
#define __POINT_H_
class Point {
private : //public에서 private로 변경
int x;
int y;
public :
bool InitMembers(int xpos, int ypos);
int GetX() const;
int GetY() const;
bool SetX(int xpos);
bool SetY(int ypos);
};
#endif
멤버변수 x,y를 private로 선언해서 임의로 값이 지정되는것을 막아놓았다.
즉, x와 y라는 정보를 은닉한 상황이다.
#ifndef #endif의 뜻
갑자기 #ifdef랑 #endif의 뜻을 모르겠어서 중간에 찾아봤다..
#ifndef는 if not define의 약자로 컴파일이 되어 있지 않을 경우의 의미라고 한다..
#endif는 #ifdef의 구문이 끝났음을 의미한다.
Point.cpp
#include <iostream>
#include "Point.h"
using namespace std;
bool Point::InitMembers(int xpos, int ypos) {
if (xpos < 0 || ypos < 0) {
cout << "벗어난 범위의 값 전달" << endl;
return false;
}
x = xpos;
y = ypos;
return true;
}
int Point::GetX() const {
return x;
}
int Point::GetY() const {
return y;
}
bool Point::SetX(int xpos) {
if (0 > xpos || xpos > 100) {
cout << "벗어난 범위의 값 전달" << endl;
return false;
}
x = xpos;
return true;
}
bool Point::SetY(int ypos) {
if (0 > ypos || ypos > 100) {
cout << "벗어난 범위의 값 전달" << endl;
return false;
}
y = ypos;
return true;
}
처음 코드와는 다르게 InitMembers, SetX,SetY 함수를 통해 멤버 변수에 잘못된 값이 저장되지 않도록 하고있다.
멤버변수를 private로 선언하고, 해당 변수에 접근하는 함수를 별도로 정의해서
안전한 형태로 멤버 변수의 접근을 유도하는 것이 '정보 은닉'이다.
Get~~, Set~~와 같은 이름의 함수들을 '엑세스 함수(access function)'라 하며,
이들은 멤버 변수를 private로 선언하면서 클래스 외부에서의 멤버 변수 접근을 목적으로 정의되는 함수들이다.
엑세스 함수는 정의만 되어있고 호출되지 않는 경우도 많다.
Rectangle.h
#ifndef __RECTANGLE_H_
#define __RECTANGLE_H_
#include "Point.h"
class Rectangle {
private :
Point upLeft;
Point lowRight;
public :
bool InitMembers(const Point& ul, const Point& lr);
void ShowRecInfo() const;
};
#endif
Rectangle.cpp
#include <iostream>
#include "Rectangle.h"
using namespace std;
bool Rectangle::InitMembers(const Point& ul, const Point& lr) {
if (ul.GetX() > lr.GetX() || ul.GetY() > lr.GetY()) {
cout << "잘못된 위치 정보 전달" << endl;
return false;
}
upLeft = ul;
lowRight = lr;
return true;
}
void Rectangle::ShowRecInfo() const {
cout << "좌 상단: " << '[' << upLeft.GetX() << ", ";
cout << upLeft.GetY() << ']' << endl;
cout << "우 하단: " << '[' << lowRight.GetX() << ", ";
cout << lowRight.GetY() << ']' << endl << endl;
}
RectangleFaultFind.cpp
#include <iostream>
#include "Point.h"
#include "Rectangle.h"
using namespace std;
int main() {
Point pos1;
if (!pos1.InitMembers(-2, 4))//false이면 "초기화 실패"출력
cout << "초기화 실패" << endl;
if (!pos1.InitMembers(2, 4))
cout << "초기화 실패" << endl;
Point pos2;
if (!pos2.InitMembers(5, 9))
cout << "초기화 실패" << endl;
Rectangle rec;
if (!rec.InitMembers(pos2, pos1))
cout << "직사각형 초기화 실패" << endl;
if (!rec.InitMembers(pos1, pos2))
cout << "직사각형 초기화 실패" << endl;
rec.ShowRecInfo();
return 0;
}
const 함수
Point.h와 Rectangle.h에 선언된 다음 함수들에는 const 선언이 추가되어 있다.
이 함수 내에서는 멤버 변수에 저장된 값을 변경하지 않겠다
라는 의미이다.
const 선언이 추가된 멤버함수 내에서 멤버변수의 값을 변경하는 코드가 있다면, 컴파일 에러가 발생한다.
이는 실수를 최소화하는데 도움이 된다.
const 함수 내에서는 const 함수가 아닌 함수의 호출이 제한된다.
const로 선언되지 않은 함수는 멤버변수에 저장된 값을 변경하지 않더라도,
변경할 수 있는 능력을 가졌기 때문에 이러한 함수의 호출을 허용하지 않는 것이다.
class EasyClass{
private:
int num;
public:
void InitNum(int n){
num = n;
}
int GetNum() //const 선언이 추가되어야 아래의 컴파일 에러 소멸
{
return num;
}
};
class LiveClass{
private:
int num;
public:
void InitNum(const EasyClass &easy)
{
num = easy.GetNum();//컴파일 에러 발생
}
};
위의 예제에서 InitNum 함수의 매개변수 easy는 const참조자인데,
이를 대상으로 const함수가 아닌 GetNum함수를 호출하면 컴파일 에러가 발생한다.
'공부 > C++' 카테고리의 다른 글
[C++] 4-4. 클래스와 배열 그리고 this 포인터 (0) | 2024.09.30 |
---|---|
[C++] 4-2. 캡슐화(Encapsulation) (0) | 2024.09.23 |
[C++] 3-3. 객체 지향 프로그래밍의 이해 (0) | 2024.09.21 |
[C++] 3-2. 클래스(Class)와 객체(Object) (1) | 2024.09.19 |
[C++] 3-1. C++에서의 구조체 (1) | 2024.09.19 |