[C++] Class 개념

정채은·2025년 6월 4일

C++ 프로그래밍

목록 보기
5/16

클래스(Class)

🔹Class란?

C++에서 클래스(class)객체 지향 프로그래밍(OOP)의 핵심 개념 중 하나로, 데이터(속성)함수(행동)를 하나의 구조로 묶는 데 사용된다.

클래스는 틀, 설계도와 같으며 변수(멤버 변수)함수(멤버 함수)가 포함되어 있다.

클래스의 기본 구조

class 클래스이름 {
public:
    // 퍼블릭 멤버 변수와 함수
private:
    // 프라이빗 멤버 변수와 함수
protected:
    // 프로텍티드 멤버 변수와 함수
};

class 내부 구현

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

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

#include <iostream>
#include <algorithm> //max 함수 사용
#include <string>
using namespace std;
class Student
{
    //동작 정의(이를 멤버함수라고 합니다)
    double getAvg()
    {
        return (kor + eng + math ) / 3.0; 
    }
    int getMax()
    {
        return max(max(kor, eng), math); 
    }
    
    //데이터 정의(이를 멤버변수라고 합니다.)
    int kor;
    int eng;
    int math;
};

2️⃣ 클래스 내부에서 멤버 함수의 선언만 작성하고, 클래스 외부에서 구현하는 방법.

#include <iostream>
#include <algorithm> //max 함수 사용
#include <string>
using namespace std;
class Student
{
    //동작 정의(이를 멤버함수라고 합니다)
    double getAvg();
    int getMaxNum();
    //데이터 정의(이를 멤버변수라고 합니다.)
    int kor;
    int eng;
    int math;
};

double Student::getAvg()
{
    return (kor + eng + math) / 3.0;
}
int Student::getMaxNum()
{
    return max(max(kor, eng), math);
    // 다른 방법 return max({ kor, eng, math });
}

주로 2번 방법을 사용한다.

접근 지정자

class 키워드를 사용할 때, 접근 지정자를 명시하지 않으면 기본적으로 private으로 설정된다.

접근 지정자접근 가능 범위설명
public어디서든 가능클래스 외부, 내부 어디서나 접근 가능
private클래스 내부만 가능클래스 외부에서는 접근 불가능 (기본값)
protected클래스 내부 및 상속받은 클래스에서 가능외부에서는 불가능하지만, 상속한 클래스에서는 접근 가능

getter와 setter

private에 있는 변수를 제어해야 할 때는 대표적인 방법으로 gettersetter를 사용한다.

멤버 변수를 바꿀 때 setter를, 값을 가져올 때 getter를 사용한다.
이렇게 하면 변수를 직접 제어하지 않고, 데이터를 안전하게 다룰 수 있다.

class Person {
private:
    int age;  // 외부에서 직접 접근 불가

public:
    // 외부에서 age 값을 읽기 위한 함수 (Getter)
    int getAge() {
        return age;
    }

    // 외부에서 age 값을 설정하기 위한 함수 (Setter)
    void setAge(int a) {
        if (a >= 0) age = a;  // 유효성 검사 가능
    }
};
int main() {
    Person p;
    p.setAge(25);              // 나이 설정
    cout << p.getAge() << endl; // 나이 출력 (25)
}

생성자

🔹생성자란?

생성자는 객체를 생성할 때마다 한 번씩 자동으로 호출되는 특별한 멤버 함수.
주로 멤버 변수의 초기화에 사용된다.

생성자의 기본 개념

  • 클래스 이름과 같아야 한다
  • 리턴 타입이 없다(void조차도 안 붙임)
  • 객체가 만들어질 때 자동으로 호출됨
class MyClass {
public:
    MyClass() {
        cout << "생성자가 호출되었습니다!" << endl;
    }
};

int main() {
    MyClass obj;  // 👉 생성자 자동 호출
}

생성자의 종류

🔸 기본 생성자

#include <iostream>
using namespace std;

class Person {
public:
  string name;
  int age;

  // 기본 생성자
  Person() {
      name = "Unknown";
      age = 0;
  }

  void display() {
      cout << "Name: " << name << ", Age: " << age << endl;
  }
};

int main() {
  Person p; // 기본 생성자 호출
  p.display();
  return 0;
}

🔸 매개변수가 있는 생성자

#include <iostream>
using namespace std;

class Person {
public:
  string name;
  int age;

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

  void display() {
      cout << "Name: " << name << ", Age: " << age << endl;
  }
};

int main() {
  Person p("Alice", 25); // 매개변수가 있는 생성자 호출
  p.display();
  return 0;
}

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

#include <iostream>
using namespace std;

class Person {
public:
    string name;
    int age;

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

    void display() {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
};

int main() {
    Person p1;              // 기본값 사용
    Person p2("Bob", 30);   // 값을 지정
    p1.display();
    p2.display();
    return 0;
}

🔸 멤버 초기화 리스트(멤버 초기화에 효율적)

class Student {
private:
    string name;
    int grade;

public:
    Student(string n, int g) : name(n), grade(g) {
        cout << "초기화 리스트로 생성됨!" << endl;
    }

    void Show() {
        cout << name << ", " << grade << "학년" << endl;
    }
};

🔸 복사 생성자

class Data {
private:
    int value;

public:
    Data(int v) : value(v) {}

    // 복사 생성자
    Data(const Data& d) {
        value = d.value;
        cout << "복사 생성자 호출!" << endl;
    }

    void Show() {
        cout << "value: " << value << endl;
    }
};

int main() {
    Data d1(100);
    Data d2 = d1;  // 복사 생성자 호출됨
    d2.Show();
}

선언부(헤더)와 구현부(소스 파일) 분리

내부 구현은 사용자가 알 필요가 없는 부분이므로 보통 클래스의 선언부(헤더)와 구현부(소스 파일)을 분리한 뒤, 필요한 곳에서 헤더를 include 하여 제공하는 방식을 사용한다.

왜 굳이 파일을 나눠서 구현하나?

쉬운 예로 책을 생각하면 쉽다.
책의 전체 구성을 설명해 주는 목차 혹은 서론 없이 바로 본론으로 들어가게 되면, 체적인 구조를 파악하기 힘들다.

헤더 파일에 class를 정의하는 것은 목차를 만든다 생각하면 되고, 소스 파일에 세부 구현하는 것은 실제 책 내용이라고 생각하면 이해가 쉽다.

C++에서는 보통 헤더 파일과 소스 파일로 구분하는데, 헤더 파일에는 선언 부를, 소스 파일에는 구현 부를 작성한다.

Student 클래스를 예로 들면, 최종적으로 파일 구조는 다음과 같이 변경된다.

student.h에 Class 정의

#ifndef STUDENT_H_
#define STUDENT_H_
class Student
{
public:
    //값이 주어지지 않을경우 기본값을 할당하는 생성자
    Student(int math = 32, int eng = 17, int kor = 52)
    {
        this->math = math;
        this->eng = eng;
        this->kor = kor;
    }
    double getAvg();
    int getMaxScore();

private:
    //데이터 정의(이를 멤버변수라고 합니다.)
    int kor;
    int eng;
    int math;
};
#endif

중복 선언 방지

📌Include Guard (인클루드 가드)

1️⃣#ifndef STUDENT_H_의 의미는 STUDENT_H_가 정의되어 있지 않은 경우에만 아래 코드를 수행하라는 의미이다.

2️⃣ #define STUDENT_H_STUDENT_H_를 정의합니다.
#ifndef일 때만 #define이 수행되므로 단 한 번만 수행될 수 있다.

3️⃣ #ifndef가 끝났다는 것을 알려주기 위해 #endif를 작성한다.

4️⃣ 최종적으로 Student Class는 중복 포함될 수 없게 된다.


📌#pragma once

// MyClass.h
#pragma once

class MyClass {
public:
    void sayHello();
};

클래스와 객체

  • 클래스는 설계도
  • 객체는 그 설계도를 기반으로 만들어진 실제 인스턴스
class Car {
public:
    string brand;
    void Drive() { cout << brand << " 주행 중!" << endl; }
};

int main() {
    Car myCar;
    myCar.brand = "BMW";
    myCar.Drive();  // BMW 주행 중!
}
profile
누군가에게 추억을 만들어주는 그날까지

0개의 댓글