C++ 공부

C++언어 기초 - 3. Class의 개념

Client Side 2025. 8. 21. 15:05

이제는 만들고 실행하는 것에만 포커스를 맞추는게 아니라 어떻게 하면 유지보수에 용이하게 만들 수 있을까를 생각해야한다. 재사용성이 높은 코드는 수정사항이 발생했을 때 영향을 적게 받는 코드다.

 

클래스, 객체, 멤버함수, 멤버변수의 관계

1. 클래스 (class) = 붕어빵 틀

  • 클래스는 붕어빵을 만드는 틀이다.
  • 어떤 붕어빵이 만들어질지 모양, 재료, 그리고 붕어빵이 어떤 동작을 할 수 있는지 등을 정의해 놓은 설계도와 같다.
  • 아직 실제로 먹을 수 있는 붕어빵은 아니다. 개념적인 틀이다.

2. 객체 (object) = 만들어진 붕어빵

  • 객체는 그 붕어빵 틀로 '실제로 만들어진 붕어빵 하나하나'를 말한다.

3. 멤버 변수 = 붕어빵 속재료

  • 클래스 안에 정의되어 있는 '데이터'부분
  • 객체마다 다를 수 있는 고유한 상태를 나타낸다.

4. 멤버 함수 = 붕어빵이 할 수 있는 동작 (굽기, 맛보기, 포장하기)

  • 클래스 안에 정의되어 있는 기능 또는 행동 부분.
  • 이 기능들은 틀 안에 정의되어 있고, 만들어진 붕어빵 객체가 그 기능들을 실제로 수행하는 것.
// 1. 클래스 정의 (붕어빵 틀 만들기)
class BankAccount {
    // 여기에 모든 멤버변수, 생성자, 멤버함수 정의
};

int main() {
    // 2. 객체 생성 (실제 붕어빵 굽기)
    BankAccount myAccount(123456, "홍길동", 1000);
    
    // 3. 객체 사용하기 (붕어빵 먹기!)
    myAccount.deposit(500);
    cout << myAccount.getBalance() << endl;
}

 

 

 

연습문제

각 학생은 국어, 영어, 수학 이렇게 3가지 점수를 가지고 있다. 학생별 평균점수와, 최대 점수를 구하시오

 

 

class의 정의

  • class는 일반적으로 멤버 함수(동작)와, 멤버 변수(데이터)로 구성됩니다.
  • 멤버변수
    • 클래스 내부에 선언된 변수
    • 객체의 상태를 나타냄
    • 보통 private으로 선언하여 직접 접근을 제한한다.
  • 멤버함수
    • 클래스 내부에 선언된 함수
    • 객체의 행동을 나타냄
    • 멤버변수를 조작하거나 객체의 기능을 구현
  • 동작 : 가장 큰 점수 반환 / 과목의 평균 계산
  • 데이터 : 각 과목의 점수
  • 클래스 이름은 보통 첫 글자부터 대문자로 시작하는 파스칼 케이스(PascalCase)를 많이 쓴다.
  • 변수 이름은 첫글자는 소문자이고 그 다음 단어부터는 대문자로 시작하는 카멜케이스(camelCase)를 많이 쓴다.

 

학생 클래스 정의

class Student
{
    //동작 정의 (이를 멤버함수라고 한다.)
    double getAvg();
    int getMaxNum();

    //데이터 정의(이를 멤버변수라고 한다.)
    int kor[3];
    int eng[3];
    int math[3];
}

 

class의 멤버 함수를 구현하는 방법 두 가지.

1. 클래스 내부에서 멤버 함수의 본문까지 직접 정의하는 방법.

2. 클래스 내부에서 멤버 함수의 선언만 작성하고 클래스 외부에서 구현하는 방법. (이 방법을 많이 쓴다.)

 

멤버함수 구현 (클래스 내부)

class Student
{
    //동작 정의 (이를 멤버함수라고 한다.)
    double getAvg()
    {
        return (kor + eng + math) / 3.0;
    }
    int getMaxNum()
    {
        return max(max(kor,eng),math));
    }

 

 

멤버함수 구현 (클래스 외부)

class Student
{
    //동작 정의 (이를 멤버함수라고 한다.)
    double getAvg();
    int getMaxNum();

    //데이터 정의(이를 멤버변수라고 한다.)
    int kor[3];
    int eng[3];
    int math[3];
};

double Student::getAvg()  // getAvg()는 Student 클래스에 속해 있는 멤버함수라고 알려주는 표현
{
    return (kor + eng + math) / 3.0;
}
int Student::getMaxNum()
{
    return max(max(kor, eng), math);
}

위에 클래스를 정의한 부분으로 헤더파일을 구현하고, 아래 부분으 소스파일을 구현할 것.

 

 

접근제어

  • 클래스의 멤버 함수나 멤버 변수에 접근할 때는 객체 뒤에 멤버 접근 연산자  .  을 사용한다.
  • 클래스는 접근 지정자를 사용하여 멤버의 접근 권한을 제어할 수 있다.
    • 접근지정자(접근제어자) : public, private, protected
      • public
        • 외부에서 자유롭게 접근 가능
        • 클래스 인터페이스를 정의할 때 사용
      • private
        • 해당 클래스 내부에서만 접근 가능
        • 외부에서 직접 접근 불가
        • 데이터 은닉(data hiding)을 위해 사용
      • protected
        • 해당 클래스와 상속받은 자식 클래스에서만 접근 가능
        • 상속 관계에서 중요
    • 접근지정자를 따로 지정하지 않으면 기본적으로 private가 지정된다.
    • private은 외부에서  .  연산자로 접근하지 못한다. 내부는 가능.
    • public은 누구나 접근 가. 보통 멤버함수(getter/setter)나 외부에서 써야 하는 인터페이스를 둔다.
    • 일반적으로 멤버함수는 public으로 접근하고, 멤버변수는 private으로 접근한다.
class Student
{
public:
    //동작 정의 (이를 멤버함수라고 한다.)
    double getAvg();
    int getMaxNum();

private:
    //데이터 정의(이를 멤버변수라고 한다.)
    int kor[3];
    int eng[3];
    int math[3];
};

 

getter와 setter

  • private에 있는 변수를 제어할 때는 어떻게 해야 하나 -> 이런 상황에서 사용하는 대표적인 방법이 바로 getter와 setter.
  • 멤버 변수를 바꿀 때는 setter를, 값을 가져 올 때는 getter를 사용한다.
  • 이렇게 하면 변수를 직접 제어하지 않고 데이터를 안전하게 다룰 수 있다.
  • 변수를 외부에 노출하면 잘못 사용할 가능성이 있고, 이를 막을 수 없다.
  • getter와 setter를 사용하는 주된 이유는 캡슐화를 구현하기 위함이다.
    • 캡슐화(Encapsulation)
      • 데이터를 보호하고 은닉
      • 유효성 검사 등 데이터 제어 가능
      • 내부 구현을 변경해도 외부 코드에 영향 없음
      • 코드 유지보수성 향상
  • getter
    • private 멤버변수의 값을 읽기 위한 public 멤버함수
    • 값을 읽기만 하는 함수. 부작용 없이 const로 만드는게 좋다.
  • setter
    • private 멤버변수의 값을 설정하기 위한 public 멤버함수
    • 값 설정 시 유효성 검사 등 추가 로직 구현 가능
    • 값을 바꾸는 함수. 유효성 검사(예: 음수 금지)를 넣어서 객체를 "항상 유효한 상태"로 유지한다.
class Student
{
public:
    //동작 정의(이를 멤버함수라고 합니다)
    double getAvg();
    int getMaxScore();

	    void setMathScore(int math)             ---------  이 아래 부분이 setter ----
    {
        this->math = math;  // this-> '클래스 내의 math' 라는 의미
    }
    void setEngScore(int eng)
    {
        this->eng = eng;
  
    }
    void setKorScore(int kor)
    {
        this->kor = kor;
    }

    int  getMathScore() { return math; }           ------- 이  아래  부분이 getter ---
    int  getEngScore() { return eng; }
    int  getKorScore() { return kor; }

 

 

생성자

  • 생성자는 객체를 생성할 때마다 한 번씩 자동으로 호출되는 특별한 멤버 함수.
  • 객체가 제대로 작동할 수 있도록 초기 설정을 해주는 역할을 한다.
  • 보통 생성자는 필요한 멤버 변수를 초기화하거나 객체가 동작할 준비를 하기 위해 사용한다.
  • class 이름과 동일한 이름을 가진 함수로 정의된다.
  • void나 int 같은 반환형이 없다. (값을 반환하는데 아니라 객체를 만드는 역할이기 때문) 
  • 일반 함수는 main함수에 적어서 호출해줘야 하는데 생성자는 객체를 만드는 순간 자동으로 호출된다.
  • 만약 클래스에 아무 생성자도 만들지 않으면, C++ 컴파일러는 친절하게 기본 생성자를 자동으로 하나 만들어준다.
  • 하지만 만약 하나라도 매개변수 있는 생성자를 직접 만들면, 컴파일러는 더 이상 기본 생성자를 자동으로 만들어주지 않는다.

 

생성자가 필요한 이유

  • 객체의 멤버 변수들을 "유의미한 값"으로 초기화해주기 위함.
  • 메모리는 기본적으로 '쓰레기 값'이라고 불리는 아무 값이나 막 들어가 있다.
  • 만약 생성자가 없다면 객체를 만들었을 때 멤버변수들에 뭐가 들어있을지 아무도 모른다.
  • 객체가 처음부터 올바른 상태로 시작하게 하려고 생성자가 꼭 필요하다.

 

생성자의 호출 순서

  •  클래스로 만든 것을 변수로 선언한 것을 객체라고 한다.
  •  변수로 선언하여 메모리에 잡히는 것을 인스턴스화 라고 한다.
  • 정의된 class를 변수로 선언하면 해당 객체가 메모리에 올라간다.
  • class는 설계도에 비유할 수 있으며, 객체 혹은 인스턴스는 설계도에 의해 만들어진 실제 결과물이다.
  • 객체가 생성될 때, 멤버 변수를 포함해서 필요한 정보들이 메모리에 올라간다.
  • 이 작업이 완료되면 생성자가 호출된다.

 

기본생성자

  • 인자가 없는 생성자.
// 기본 생성자
Person() {
    name = "Unknown";
    age = 0;
}

 

매개변수가 있는 생성자

// 매개변수가 있는 생성자
Person(string n, int a) {
    name = "n";
    age = a;
}

 

기본매개변수가 있는 생성자

// 기본 매개변수가 있는 생성자
Person(string n = "DefaultName", int a = 18) {
    name = "n";
    age = a;
}
  • 인자를 전달하지 않으면 기본 매개변수 값으로 들어가고, 인자를 전달하면 인자가 값으로 들어간다.