[C++] 클래스

강민석·2022년 10월 18일
0

이것이 C++이다

목록 보기
2/8
post-thumbnail

3.1 객체지향 프로그래밍 개요

3.2 클래스 기본 문법

#include <iostream>

class UserData {
    // 접근 제어 지시자(public / protected / private)
    public:
        // 멤버 변수 선언
        int age;
        char name[32];

        // 멤버 함수 선언 및 정의
        void print(void) {
            printf("%d, %s\n", age, name);
        }
}

int main(int argc, char* argv[]) {

    UserData user = {24, "강민석"};

    user.print();

    return 0;
}

3.2.1 멤버 선언 및 정의

  • 클래스의 멤버 변수는 '생성자(constructor)'를 통해 초기화 할 수 있다는 점이 구조체와의 큰 차이점이다.

  • 멤버 변수를 초기화 하는 방법 세 가지

    1. 생성자를 통한 초기화
    2. 생성자 초기화 목록 사용
    3. 선언과 동시에 초기화(C++11 이상)
#include <iostream>

// 1. 생성자를 통한 초기화
class Example1 {
    public:
        Example1() {
            std::cout << "생성자 함수를 호출하여 멤버변수를 초기화합니다." << std::endl;
            data = 10;
        }

        int data;

        void printData(void) {
            std::cout << "data: " << data << std::endl;
        }
}

// 2. 생성자 초기화 목록 사용
class Example2 {
    public:
        Example2
            : data1(10), data2(20)
        { }

        int data1;
        int data2;

        void printData(void) {
            std::cout << "data1: " << data1 << std::endl;
            std::cout << "data2: " << data2 << std::endl;
        }
}

// 3. 선언과 동시에 초기화(C++11 이상)
class Example3 {
    public:
        Example3() {}

        int data1 = 10;
        int data2 = 20;

        void printData(void) {
            std::cout << "data1: " << data1 << std::endl;
            std::cout << "data2: " << data2 << std::endl;
        }
}
  • 멤버 함수의 선언부와 정의부의 분리
class Example {
    public:
        Example() {
            data = 10;
        }

        int data;

        // 멤버 함수 선언. 정의부는 클래스의 외부에 분리할 수 있다.
        void printData(void);
}

void Example::printData(void) {
    std::cout << "data: " << data << std::endl;
}

3.2.2 접근 제어 지시자

  • public: 멤버에 관한 모든 외부 접근 허용
  • protected: 멤버에 관한 모든 외부 접근 차단. 단, 상속 관계에 있는 파생 클래스에서의 접근은 허용
  • private: 외부 접근뿐만 아니라 파생 클래스로부터의 접근까지 모두 차단.(접근제어자 미기술시 기본값으로 적용)

3.3 생성자와 소멸자

  • '생성자(Constructor)'와 '소멸자(Destructor)'는 클래스 객체가 생성 및 소멸될 때 자동으로 호출되는 함수다.
  • 매개변수가 하나도 없는 생성자를 '디폴트 생성자'라고 한다.
    • 클래스 제작자가 디폴트 생성자와 소멸자를 기술하지 않아도 컴파일러가 알아서 만들어 넣는다.
    • 새로운 생성자를 만들면서 디폴트 생성자를 생략할 수 있다.
  • 소멸자는 다중 정의가 불가능하다.
  • 지역 변수는 선언된 블록 범위가 끝나면 자동으로 소멸된다. 즉, 블록 범위의 종료 이후에 소멸자가 호출된다.
  • C++에서는 전역 변수로 선언한 클래스의 생성자가 main()함수보다 먼저 호출된다.
#include <iostream>
using namespace std;

class Example {
    int data;
    public:
        Example(int param): data(param) {
            cout << "initial data: " << param << endl;
        }

        ~Example() {
            cout << "call destructor" << endl;
        }
}

int main(int argc, char* argv[]) {
    cout << "begin main" << endl;
    Example a(1);
    cout << "before b" << endl;
    Example b(2);
    cout << "end main" << endl;
}

// 출력
// begin main
// initial data: 1
// before be
// initial data: 2
// end main
// call destructor
// call destructor

3.3.1 동적 객체의 생성과 소멸

  • 클래스의 인스턴스는 new 연산을 통해 동적으로 선언할 수 있으며, 이 경우 delete연산을 통해 삭제해야 한다.
    • 즉, 동적 객체는 생성 및 소멸 시점을 코드에서 정확하게 알 수 있다.

3.3.2 참조 형식 멤버 초기화

  • 클래스의 멤버 변수는 참초 형식으로 선언할 수 있다.
  • 참조자는 원본 대상을 변경할 수 없다.
  • 참조자는 반드시 선언과 동시에 초기화 되어야 하므로, '생성자 초기화 목록'을 통해 초기화 해주어야 한다.
Example(int &param) { data = param } // data=param 구문은 초기화가 아닌 단순 대입이므로 오류 발생
Example(int param): data(param) { } // 함수의 매개변수는 반환과 동시에 소멸되므로, 쓰레기값만 남게 된다. 즉, 반드시 참조형으로 전달해줘야함.

3.3.3 생성자 다중 정의

  • 함수의 다중 정의와 같다. 매개변수만 다르면 다른 함수로 간주함.
  • 일반적인 함수와 마찬가지로, 다중정의는 유지보수의 어려움을 초래한다.
    • C++11부터 지원되는 '생성자 위임'을 통해 어느정도 해결 가능.
    • '생성자 초기화 목록'에서 다른 생성자를 추가로 부를 수 있다.

3.3.4 명시적 디폴트 생성자

  • 멤버 함수와 마찬가지로, 클래스의 생성자 또한 선언부 및 정의부를 분리할 수 있다.
  • 다음과 같은 방법으로도 디폴트 생성자의 선언과 정의를 명백히 분리할 수 있다.
class Example {
    public:
        // 디폴트 생성자 선언 및 정의
        Example(void) = default;
        int data = 5;
}

디폴트 생성자의 선언 및 정의를 명백히 분리한다는 말의 의미가 잘 와닿지 않는다...
당장은 이런 문법이 존재한다는것만 알아두라고 한다. '템플릿'에서 쓰인다고 하니 그때 자세히 알아보자.

  • 생성자 다중 정의를 통해 새로운 생성자만을 기술할 경우, 디폴트 생성자는 사라진다.
  • 다음 코드를 작성함으로써 명시적으로 디폴트 생성자가 존재하지 않음을 나타낼 수 있다.
    Example(void) = delete;

3.4 메서드

  • 클래스의 멤버 함수를 '메서드'라고 한다.

3.4.1 this 포인터

  • this포인터는 작성 중인 클래스의 실제 인스턴스에 대한 주소를 가리키는 포인터다.
class Example {
    public:
        Example(int param): data(param) {}
        int data;

        void printData() {
            cout << "data: " << this -> data << endl;
        }
}

3.4.3 상수형 메서드

  • 상수형 메서드(혹은 상수화된 메서드)는 멤버 변수에 읽기 접근은 가능하지만 쓰기는 허용되지 않는 메서드를 말한다.
  • 함수 원형 뒤에 const예약어만 붙이면 된다.
  • 상수형 메서드 내부에서는 상수형 메서드가 아닌 멤버를 절대 호출할 수 없다.

3.4.5 상수형 메서드의 예외 사항

  • mutable로 선언함 멤버 변수의 값은 상수형 메서드에서도 쓰기가 허용된다.
  • const_cast<>를 사용하여 상수형 참조를 억지로 변경할 수 있다.

3.4.6 멤버 함수 다중 정의

  • 메서드 또한 다중 정의할 수 있다.
  • delete 예약어를 통해 명시적으로 메서드를 삭제하여 원치않는 형식의 실인수가 넘어오는 것을 미연에 방지할 수 있다.

3.4.7 정적 멤버

  • 정적 멤버는 사실상 전역 변수 및 함수와 동일하다.
  • 인스턴스를 선언하지 않고 직접 접근할 수 있다.
  • this 포인터를 사용할 수 없으며, 정적 변수는 반드시 선언과 정의를 분리해야 한다. -> 그럼 참조형 정적 변수는 존재할 수 없다는건가?

0개의 댓글