[C++] 객체, 함수 오버로딩

2023. 8. 13. 15:43Programming Language/C++

728x90
  • 객체

객체 (Object)
객체는 데이터와 해당 데이터를 처리하는 함수(메서드)를 하나로 묶은 개념이다. 객체 지향 프로그래밍에서 프로그램의 구성 요소로 객체를 사용하여 현실 세계의 개념을 모델링하고 문제를 해결한다. 각 객체는 특정한 역할과 책임을 가지며, 객체 간의 상호작용을 통해 프로그램이 동작한다.

객체는 속성(멤버 변수)과 동작(메서드)으로 이루어진다. 예를 들어, 자동차 객체는 속성으로 브랜드, 모델, 색상 등을 가지며, 동작으로 주행, 정지, 가속 등을 수행할 수 있다.

 

 

  • 추상화

추상화 (Abstraction)
추상화는 복잡한 현실 세계를 단순화하여 중요한 특징과 기능을 강조하는 과정이다. 객체 지향 프로그래밍에서 추상화는 객체의 공통된 특징을 추출하여 클래스로 표현하는 과정을 말한다. 즉, 클래스는 객체를 생성하기 위한 템플릿이며, 객체들 간에 공통된 속성과 동작을 정의한다.

예를 들어, 여러 종류의 동물을 모델링할 때 "동물"이라는 추상적인 개념을 정의할 수 있습니다. 이 추상 클래스에는 모든 동물이 가지는 공통된 특성과 동작을 포함할 수 있다. 실제 동물 객체는 이 추상 클래스를 상속받아 구체적인 특성을 추가한 후 객체를 생성하게 된다.

  • 객체와 추상화의 관계

객체 지향 프로그래밍에서 추상화는 객체들 간에 공통된 속성과 동작을 정의하고, 이를 기반으로 클래스를 생성하는 과정이다. 클래스는 객체의 추상화를 나타내는 도구로 사용되며, 객체를 실체화한 인스턴스를 생성하는 데 사용된다. 추상화를 통해 프로그램의 복잡성을 낮추고, 객체들 간의 관계와 상호작용을 보다 명확하게 표현할 수 있다.

이렇게 객체와 추상화는 객체 지향 프로그래밍에서 중요한 개념으로, 현실 세계의 복잡한 문제를 단순화하고 해결하기 위한 강력한 도구로 활용된다.

 

 

  • 인스턴스 변수와 인스턴스 함수

추상화에서 인스턴스 변수와 인스턴스 함수는 클래스의 중요한 구성 요소로서, 객체의 상태와 동작을 정의하는 역할을 한다.


1. 인스턴스 변수 (Instance Variables)
인스턴스 변수는 객체의 상태나 데이터를 저장하는 데 사용된다. 클래스 내에서 정의되며, 객체가 생성될 때마다 해당 변수들은 객체의 상태를 나타내는 값들로 초기화된다. 객체마다 서로 다른 값을 가질 수 있으며, 객체의 특성을 나타내는 데이터를 저장하는 역할을 한다.

 

class Car {
public:
    int speed; // 인스턴스 변수
    int year;  // 인스턴스 변수
};

int main() {
    Car myCar1;
    myCar1.speed = 100;
    myCar1.year = 2022;

    Car myCar2;
    myCar2.speed = 120;
    myCar2.year = 2020;

    return 0;
}

위의 코드에서 speed와 year는 Car 클래스의 인스턴스 변수이다. 각각의 myCar1과 myCar2 객체는 서로 다른 값을 가지며, 객체마다 독립적인 상태를 나타낸다.

 

 

2. 인스턴스 함수 (Instance Functions 또는 Methods)
인스턴스 함수는 객체의 동작이나 행동을 정의하는 데 사용된다. 클래스 내에서 정의되며, 객체가 호출할 수 있는 함수이다. 객체의 상태(인스턴스 변수)를 변경하거나 특정 작업을 수행하는 데 사용된다. 인스턴스 함수는 객체의 특성과 행동을 결합하여 객체의 기능을 구현한다.

 

class Car {
public:
    int speed; // 인스턴스 변수

    void accelerate() {
        speed += 10;
    }
};

int main() {
    Car myCar;
    myCar.speed = 50;
    myCar.accelerate(); // 인스턴스 메소드 호출
    // 이제 myCar.speed는 60이 됨

    return 0;
}

위의 예시에서 accelerate()는 Car 클래스의 인스턴스 메소드로, 속도를 증가시키는 작업을 수행한다. 이 메소드는 객체의 특정 상태(speed 변수)를 변경하며 동작한다.

 

3. 인스턴스 변수와 인스턴스 메소드의 관계
인스턴스 변수와 인스턴스 메소드는 클래스 내에서 함께 정의되어 객체의 상태와 동작을 모델링한다. 인스턴스 메소드는 인스턴스 변수의 값을 읽고 수정하여 객체의 상태를 변경하거나 조작할 수 있다. 이를 통해 객체의 특성과 행동이 결합되어 객체의 실제 동작을 구현하는데 사용된다. 이렇게 객체 지향 프로그래밍에서는 데이터와 그 데이터를 조작하는 함수들이 함께 묶여 객체를 현실 세계의 모델로 만들어내는 중요한 원리이다.

 


 

#include <iostream>

class Car {
private:
    // private 맴버 변수
    std::string brand;
    int year;
    int speed;

public:
    // 생성자
    Car(const std::string &b, int y) : brand(b), year(y), speed(0) {}

    // public 맴버 함수
    void accelerate() {
        speed += 10;
        std::cout << "Accelerating. Current speed: " << speed << " km/h" << std::endl;
    }

    void brake() {
        if (speed >= 10) {
            speed -= 10;
            std::cout << "Braking. Current speed: " << speed << " km/h" << std::endl;
        } else {
            std::cout << "Car is already stopped." << std::endl;
        }
    }

    void displayInfo() {
        std::cout << "Brand: " << brand << ", Year: " << year << ", Speed: " << speed << " km/h" << std::endl;
    }
};

int main() {
    // 객체 생성 및 초기화
    Car myCar("Sonata", 2022);

    // 맴버 함수 호출
    myCar.accelerate(); // 가속
    myCar.accelerate(); // 가속
    myCar.brake();      // 감속
    myCar.displayInfo(); // 정보 출력

    return 0;
}

위의 코드에서 Car 클래스는 자동차 객체를 모델링하기 위해 정의되었다. private 섹션 아래에는 클래스 내부에서만 접근 가능한 brand, year, 그리고 speed 맴버 변수가 선언되어 있다. 이렇게 선언된 변수는 클래스 외부에서 직접 접근할 수 없으며, 맴버 함수를 통해 간접적으로 조작된다.

public 섹션 아래에는 생성자와 accelerate(), brake(), displayInfo()와 같은 맴버 함수가 선언되어 있다. 이 함수들은 클래스 외부에서 접근 가능하며, 객체의 상태를 변경하거나 정보를 출력하는 역할을 수행한다.

 

 

  • private, public

private와 public은 C++ 클래스의 멤버 변수와 멤버 함수의 접근 제어 지시자입니다. 이들은 클래스 내부 및 외부에서의 접근 권한을 결정하는 역할을 합니다.

  • private 접근 제어 지시자

private로 선언된 멤버 변수 또는 멤버 함수는 해당 클래스 내에서만 접근 가능하다. 외부에서는 직접 접근이 불가능하며, 클래스 내부에서만 호출하거나 조작할 수 있다. 이는 클래스 내부의 구현 세부 사항을 외부로부터 은닉하고, 클래스의 불변성과 내부 동작을 보호하기 위해 사용다.

 

 

  • public 접근 제어 지시자

public으로 선언된 멤버 변수 또는 멤버 함수는 클래스 내부와 외부에서 모두 접근 가능하다. 외부에서도 객체를 통해 멤버 변수에 접근하거나 멤버 함수를 호출할 수 있다. 이는 클래스의 외부 사용자에게 필요한 기능을 제공하고, 클래스의 인터페이스를 정의하는 데 사용된다.

 

#include <iostream>

class MyClass {
    int Var; // private 멤버 변수 (default)

private:
    int privateVar; // private 멤버 변수

public:
    int publicVar; // public 멤버 변수

    MyClass() {
        Var = 0;
        privateVar = 0;
        publicVar = 0;
    }

    void setPrivateVar(int value) {
        privateVar = value;
    }

    int getPrivateVar() {
        return privateVar;
    }

    void printInfo() {
        std::cout << "Private Variable: " << privateVar << ", Public Variable: " << publicVar << ", Default Variable: " << Var << std::endl;
    }
};

int main() {
    MyClass obj;

    // public 멤버 변수에 접근 및 변경 가능
    obj.publicVar = 42;

    // private 멤버 변수에 접근 불가능 (컴파일 오류)
    // obj.privateVar = 10;

    obj.setPrivateVar(10); // private 멤버 변수에 접근 가능한 함수 호출

    obj.printInfo(); // 정보 출력

    return 0;
}

위 코드에서 privateVar는 private으로 선언되어 클래스 내부에서만 접근 가능하다. publicVar는 public으로 선언되어 클래스 내부와 외부 모두에서 접근 가능하다. setPrivateVar()와 getPrivateVar() 함수는 privateVar에 접근하기 위한 인터페이스 역할을 수행한다.

 

int Var;는 private으로 간주되며, public, private로 명시하지 않은 멤버 변수들은 모두 private으로 취급된다. 이 변수는 클래스 외부에서는 직접 접근할 수 없다. 출력 결과에서 Var 변수의 값이 0으로 출력됨을 확인할 수 있다.

 


  • 함수 오버로딩

함수 오버로딩은 하나의 클래스 내에서 함수 이름은 같지만 매개변수의 개수나 타입이 다른 여러 개의 함수를 정의하는 것을 말한다.

#include <iostream>

void foo(int value) {
    std::cout << "foo(int): " << value << std::endl;
}

void foo(double value) {
    std::cout << "foo(double): " << value << std::endl;
}

int main() {
    foo(5);        // 1. 정확한 일치, foo(int) 호출
    foo(3.14);     // 1. 정확한 일치, foo(double) 호출
    foo('A');      // 2. 형변환 (char -> int), foo(int) 호출
    foo(10u);      // 2. 형변환 (unsigned int -> int), foo(int) 호출
    foo(2.5f);     // 2. 형변환 (float -> double), foo(double) 호출
    foo(true);     // 2. 형변환 (bool -> int), foo(int) 호출
    foo(nullptr);  // 3. 포인터 변환, foo(int) 호출

    return 0;
}
  1. 정확한 일치 (Exact Match):
    매개변수 타입이 호출 시 전달되는 인자와 정확히 일치하는 함수가 있는 경우 해당 함수가 선택됩니다. 예시에서 foo(int)와 foo(double) 함수가 해당한다.

  2. 형변환 (Type Conversion):
    정확한 일치하지 않는 경우, 컴파일러는 타입 변환을 통해 일치하는 함수를 찾는다. 작은 크기의 정수형은 더 큰 크기의 정수형으로, float은 double로 변환된다. 이러한 형변환을 통해 매개변수 타입과 가장 잘 맞는 함수를 선택한다.

  3. 포인터 변환 (Pointer Conversion):
    만약 인자가 포인터라면, nullptr와 같이 포인터 타입이나 숫자 타입으로 변환 가능한 값은 포인터 타입으로 변환된다.

  4. 유저 정의된 타입 변환 (User-defined Type Conversion):
    이 단계에서는 사용자가 정의한 타입 변환 연산자를 사용하여 일치하는 함수를 찾는다.

함수 오버로딩은 여러 단계를 통해 가장 적합한 함수를 선택하게 된다. 만약에 컴파일러가 위 과정을 통해도 가장 적합한 함수를 선택하지 못하거나 모호한 경우, 컴파일러는 오류를 발생시킨다.

 


C++에서는 클래스 내부에 함수의 선언만을 미리 작성하고, 함수의 내용은 클래스 외부에서 별도로 정의하는 것을 "함수 정의의 분리" 또는 "메서드 정의의 분리"라고 한다. 이렇게 하면 클래스 선언 부분과 클래스 구현 부분을 분리하여 코드의 모듈성과 가독성을 향상시킬 수 있다.

이러한 기법은 특히 클래스의 인터페이스와 구현을 분리하고, 코드의 변경이 필요한 경우 클래스의 구현 부분만 수정하는데 용이하다. 이는 소스 코드의 수정 범위를 최소화하여 오류 가능성을 줄이고, 팀 작업이나 유지 보수 작업을 보다 효율적으로 수행할 수 있게 해준다.

#include <iostream>

// 클래스 선언 부분 
class MyClass {
public:
    void printMessage();  // 함수 선언만 작성
};

// 클래스 구현 부분 
void MyClass::printMessage() {
    std::cout << "Hello from printMessage!" << std::endl;  // 함수의 내용 정의
}

// 메인 함수 
int main() {
    MyClass obj;
    obj.printMessage();  // MyClass의 printMessage 함수 호출

    return 0;
}

위 코드에선 Myclass:: 를 통해 함수 이름 앞에 붙여주게 되면 이 함수가 "Myclass 클래스의 정의된 함수"라는 의미를 부여하게 된다. 만약 

void printMessage(){...

와 같이 작성한다면 위 함수는 클래스의 맴버함수가 아닌 그냥 일반적인 함수가 된다.

'Programming Language > C++' 카테고리의 다른 글

[C++] 쓰레드 (thread)  (1) 2023.08.13
[C++] 참조자  (0) 2023.08.13
[C++] std, namespcae  (0) 2023.08.12
[C++] STL  (0) 2023.08.11