C++로 OOP 복습하기(1) - 캡슐화(Encapsulation), 접근 지정자(Access Specifier), 접근 함수(Access Function)

kimseyoung·2023년 5월 6일
0

C, C++

목록 보기
4/6

캡슐화(Encapsulation)

구조체 돌아보기(Struct)

캡슐화의 뜻을 이해하기 전에 C에서 많이 사용하던 구조체(struct)를 잠시 보자.

struct Struct1
{
	int i;
    double d;
    char c;
    
    int (*foo)(int a, int b);
    void(int(*bar)[])(int);
};

C언어에서 자주 사용하던 구조체는 다음과 같이 사용했다. struct 내부의 멤버들의 타입들을 지정해 줌으로서 사용할 수 있었다. C++의 Class는 이 Struct에서 struct 지정자를 class로 바꿈으로서 시작된다.

class Struct1
{
	int i;
    double d;
    char c;
    
    int (*foo)(int a, intb);
    void(int(*bar)[])(int);
};

하지만, class에는 지금 설명하는 캡슐화(Encapsulation)을 사용할 수 있다. 캡슐화란 일반적으로 연관있는 변수와 함수를 클래스로 묶는 것과 더불어, 객체의 데이터를 외부로부터 감출 수 있고, 기능 구현을 은닉할수 있다. (정보은닉)

캡슐화를 실현하기 위해 사용하는 것들이 바로 접근 지정자(Access Specifier)접근 함수(Access Function) 이다.

접근 지정자(Access Specifier)

접근 지정자는 총 세가지로 public, private, protected가 존재한다. 클래스 내부에서 다음처럼 사용한다.

class Class1
{
private: // 기본적으로 클래스 내부 멤버는 private로 선언된다.
	int 	p_i;
    double  p_d;
    char	p_c;
    void    foo() {};

public:
	int		pb_i;
    double	pb_d;
    char	pb_c;
    void 	pub_foo() {};

protected:
	int 	pt_i;
    double	pt_d;
    char	pt_c;
    void 	pro_foo() {};
    
}

하나씩 그 범위를 알아보자

  • public
    public 지정자에 묶인 클래스 멤버들은 전체 공개되어, 접근이 자유롭다.
  • private
    private 지정자에 묶인 클래스 멤버들은 일절 공개되지 않아, 접근을 할 수 없다.
  • protected
    추 후, 상속을 다룰 때 자세히 설명하겠지만, 상속된 클래스는 부모의 private에도 접근 할 수 없다. protected 내부에 선언한다면, 상속된 클래스도 접근 가능하다.

protected는 추후 상속에서 더욱 다루도록 하고, public과 private에 시점을 옮겨보자.

private 내부의 멤버에 접근하려면 어떡해야할까? 굳이 접근 해야하는 멤버를 private으로 선언하는것이 아니라 그냥 public으로 선언하면 되지 않을까? 하지만, 이는 큰 혼란을 가져올 수 있다.

캡슐화의 초점은 클래스 내부 메소드의 구현만 변경하면 되게끔 하는 것이다. 즉, 어느 한 기능이 수정 되었을때, 소스코드 전체를 수정하는 것이 아니라. 그 기능을 멤버로 지니고 있는 클래스 내부만 수정하면 된다는 소리이다.

이 예를 들기 앞서서 접근 함수(Access Function)을 예로 들어 설명한다. 접근 함수는 private 멤버에 접근하기 위해, public이나 protected 범주에 선언하는 함수이다. 대표적으로 Getter/Setter를 예로 들 수 있다.

class Class1
{
	char target; // Basically Private
    
    public:
    	void setTarget(const char& input)
        {
        	target = input;
        }
        
        const char& getTarget()
        {
        	return target;
        }
}
...

int main()
{
	Class1 testClass;
    
    testClass.setTarget(3);
    
    std::cout << testClass.getTarget() << endl;
    
	return 0;
}

다음과 같이 사용한다. setTarget 메서드를 통해 private 멤버인 target 변수를 수정하고, getTarget을 이용해 받는다. 이러한, private 멤버와 외부를 연결 시켜주는 메서드, 함수를 접근 함수라고 한다.

그렇다면, 위에서 언급한 캡슐화의 초점을 위해 접근 함수가 기여하는 일이 무엇일까? 다음 예제를 확인해보자.

<클래스를 사용한 캡슐화>

class Person
{
	string first_name = "Seyoung";
    string last_name = "Kim";
    int    age = 28;
    int    height = 191;
    int	   weight = 80;    
    
    public:
    	void printPersonInfo()
        {
        	cout << first_name << " " << last_name << " " << age ... 생략
        }
}

<구조체를 이용>
struct sPerson
{
	string first_name;
    string last_name;
    int    age;
    int    height;
    int    weight;
};

int main()
{
	// 외부 사용 예시
    struct sPerson person1;
    Person person2;
    
    <구조체를 사용할 >
    person1.last_name = ...
    person1.first_name = ...
    ...
    for(int i = 0; i < 5; ++i)
    {
    	cout << *(&person+i) << " ";
    }
    cout << endl;
    
    
    <클래스 사용시>
    // Getter Setter 생략(클래스에서 임시로 미리 기본값 지정)
    person2.printPersonInfo();
    
	return 0;
}

위의 예시를 보자, 클래스를 사용하면, 구조체를 이용해 만든 것과 같은 기능을 하더라도, 외부에서는 메서드 하나로 표현 할 수 있게 된다. 여기서, 만약 printPersonInfo() 메서드에, 각각의 출력값이 무엇을 의미하는지 메서드를 수정해야한다고 생각해보자, 또, Person정보 안에 가족의 수라는 새로운 변수가 생긴다면? 그리고 이러한 메서드가 전반적으로 여러번 사용된다면? 구조체를 이용한 경우 어떻게 하겠는가?

구조체의 멤버를 추가하고, 출력하는 곳 마다. 일일이 수정을 거쳐야 할 것이다.

클래스를 사용한 경우는 printPersonInfo() 라는 클래스 내부의 메서드 한줄과 클래스 멤버만 수정해주면 된다. 이렇게 private과 접근 함수를 통해 캡슐화를 실현시킬 수 있다. 이는 정보은닉과, 객체지향의 재사용성을 실현시킴을 알 수 있다.

profile
Back-end Developer, DevOps Engineer

0개의 댓글