[디딤돌 C++] 15. 접근 지정자

이번에는 멤버의 가시성을 설정하는 접근 지정자에 관해 알아볼게요.

C언어의 구조체는 모든 곳에서 모든 멤버를 접근할 수 있어요. 이러한 특징은 개발자가 멤버에 접근하기 쉬워서 구현하기 쉽게 생각할 수 있지만 시나리오에 맞게 데이터를 유지하는 것은 어려울 수 있어요.

예를 들어 설명할게요.

프로그램에서 이름, 번호, 아이큐를 멤버로 갖는 학생 형식이 있다고 가정합시다.

학생 개체는 생성할 때 이름과 번호를 부여하고 아이큐는 100으로 설정하기로 해요. 그리고 학생이 공부하면 아이큐가 공부한 시간만큼 증가하게 만들거예요. 단 아이큐는 300을 넘지 않게 하기로 해요.

그런데 학생 개체를 사용하는 곳에서 학생 구조체의 멤버에 접근할 수 있어서 “공부하다.” 기능을 호출하지 않고 직접 아이큐를 조절하면 범위를 벗어날 수 있어요. 이와 같은 버그가 발생하였을 때 이를 발견하고 수정하는데 비용이 발생하겠죠.

//Student.h
#pragma once
#include <string.h>
#include <stdio.h>
#define MAX_NAME_LEN    20
#define DEF_IQ  100 //디폴트 IQ
#define MAX_IQ 300 //최대 IQ
typedef struct Student Student;
struct Student
{
    int num;
    char name[MAX_NAME_LEN];
    int iq;
};
void Student_Student(Student *stu,int num, const char *name); //생성자
void Student_Study(Student *stu, int hours);//공부하다.
void Student_View(Student *stu);//학생 정보 출력
//Student.c
#include "Student.h"
void Student_Student(Student *stu,int num, const char *name)
{
    stu->num = num;
    strcpy_s(stu->name,MAX_NAME_LEN,name);
    stu->iq = DEF_IQ;
}
void Student_Study(Student *stu, int hours)
{
    printf("%s 학생 %d시간 공부하다.\n",stu->name, hours);
    if( stu->iq + hours >MAX_IQ)
    {
        stu->iq = MAX_IQ;
    }
    else
    {
        stu->iq += hours;
    }
}
void Student_View(Student *stu)
{
    printf("번호:%d, 이름:%s, 아이큐:%d\n",stu->num,stu->name,stu->iq);
}
//Program.c
#include "Student.h"
int main()
{
    Student stu;
    Student_Student(&stu,34,"홍길동");
    Student_View(&stu);
    Student_Study(&stu,50);
    Student_View(&stu);
    stu.iq += 300; //멤버에 직접 접근하여 값을 설정
    Student_View(&stu);
    return 0;
}

▷실행 결과

번호:34, 이름:홍길동, 아이큐:100
홍길동 학생 50시간 공부하다.
번호:34, 이름:홍길동, 아이큐:150
번호:34, 이름:홍길동, 아이큐:450

위 예제에서 main 함수에서 직접 학생의 멤버 iq에 접근하여 설정하지 않고 Student_Study 함수를 사용했다면 학생의 IQ는 최대값을 넘지 않아요. 결국 C언어에서는 어디에서나 구조체의 멤버에 접근할 수 있어서 발생한 버그예요.

C++언어에서는 멤버의 가시성을 설정하는 접근 지정자 문법을 제공하고 있어요.

class 정의할 때 private, protected, public 키워드와 콜론(:)을 지정하여 이 후의 작성한 멤버의 가시성을 설정할 수 있어요.

private 접근 지정은 형식 내부에서만 접근할 수 있습니다.

public은 모든 곳에서 멤버에 접근할 수 있습니다.

protected는 파생 형식에서도 접근할 수 있습니다. 이 부분은 상속을 다루면서 설명하기로 해요.

클래스는 디폴트 가시성이 private이고 구조체는 public입니다.

class Student
{
    int num;
    string name;
    int iq;
public:
    Student(int _num,string _name);
    void Study(int hour);
    void View();
};

클래스의 디폴트 접근 지정은 private입니다. 위처럼 정의하면 num과 name, iq는 디폴트로 접근 지정합니다.

그리고 public: 지정한 뒤에 세 개의 멤버는 접근 지정이 public입니다.

따라서 형식 외부에서 num, name, iq 멤버에 접근할 수 없어요.

private 멤버 액세스

다음의 코드는 C++언어에서 접근 지정을 통해 신뢰성을 강화한 코드입니다. 멤버 iq의 접근 지정을 디폴트인 private으로 설정하여 형식 외부에서 접근할 수 없게 만들었어요.

아직 멤버 메서드 캡슐화를 다루지 않은 상태여서 모두 이해할 수는 없지만 코드를 한 번 보시고 따라서 작성 및 분석해 보세요.

//Student.h
#pragma once
#include <string>
using namespace std;

#define DEF_IQ  100 //디폴트 IQ
#define MAX_IQ 300 //최대 IQ

class Student
{
    int num;
    string name;
    int iq;
public:
    Student(int _num,string _name);
    void Study(int hour);
    void View();
};
//Student.cpp
#include "Student.h"
#include <iostream>
using namespace std;

Student::Student(int _num,string _name)
{
    num = _num;
    name = _name;
    iq = DEF_IQ;
}
void Student::Study(int hour)
{
    cout<<name<<" 학생 "<<hour<<"시간 공부하다."<<endl;
    if (iq > MAX_IQ)
    {
        iq = MAX_IQ;
    }
}
void Student::View()
{
    cout<<"번호:"<<num<<", 이름:"<<name<<", 아이큐:"<<iq<<endl;
}
//Program.cpp
#include "Student.h"
int main()
{
    Student stu(34,"홍길동");
    stu.View();
    stu.Study(50);
    stu.View();
    //stu.iq += 300; //private 접근 지정한 멤버는 형식 외부에서 접근 못 함
    stu.View();
    return 0;
}

▷실행 결과

번호:34, 이름:홍길동, 아이큐:100
홍길동 학생 50시간 공부하다.
번호:34, 이름:홍길동, 아이큐:150
번호:34, 이름:홍길동, 아이큐:150