ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [C++] 연산자 다중 정의 (Operator Overloading)
    프로그래밍/C & C++ 2024. 7. 8. 22:32
    728x90
    반응형

    연산자 다중 정의 (Operator Overloading)

    연산자 다중 정의는 특정 클래스에 대해 기존의 C++ 연산자를 재정의하여, 사용자 정의 타입도 기본 타입처럼 연산할 수 있도록 하는 기능입니다. 이를 통해 코드를 더 직관적이고 가독성 있게 만들 수 있습니다.

    주요 규칙

    1. 새로운 연산자를 정의할 수 없습니다: 기존 연산자만 오버로딩할 수 있으며, 새로운 연산자는 정의할 수 없습니다.
    2. 기존 연산자의 의미를 변경하지 않습니다: 기존 연산자의 의미를 유지하면서, 클래스에 맞게 동작하도록 합니다.
    3. 적절한 반환 타입을 지정합니다: 연산의 결과를 반환해야 하며, 반환 타입을 잘 지정해야 합니다.
    4. 적절한 접근 지정자 사용: 연산자 함수가 클래스 내부 데이터를 다룰 때 필요한 접근 권한을 고려합니다.

    MyString 클래스 예제

    이 예제에서는 문자열을 다루는 MyString 클래스를 정의하고, 다양한 연산자를 오버로딩합니다. 또한, 기존의 strcpy 대신 strcpy_s를 사용하여 안전하게 문자열을 복사합니다.

    MyString 클래스 정의

    #include <iostream>
    #include <cstring>
    
    class MyString {
    private:
        char* str;
    
    public:
        // 기본 생성자
        MyString() : str(nullptr) {}
    
        // 문자열을 인수로 받는 생성자
        MyString(const char* s) {
            if (s) {
                size_t length = strlen(s) + 1;
                str = new char[length];
                strcpy_s(str, length, s);
            } else {
                str = nullptr;
            }
        }
    
        // 복사 생성자
        MyString(const MyString& other) {
            if (other.str) {
                size_t length = strlen(other.str) + 1;
                str = new char[length];
                strcpy_s(str, length, other.str);
            } else {
                str = nullptr;
            }
        }
    
        // 이동 생성자
        MyString(MyString&& other) noexcept : str(other.str) {
            other.str = nullptr;
        }
    
        // 소멸자
        ~MyString() {
            delete[] str;
        }
    
        // 복사 대입 연산자
        MyString& operator=(const MyString& other) {
            if (this != &other) {
                delete[] str;
                if (other.str) {
                    size_t length = strlen(other.str) + 1;
                    str = new char[length];
                    strcpy_s(str, length, other.str);
                } else {
                    str = nullptr;
                }
            }
            return *this;
        }
    
        // 이동 대입 연산자
        MyString& operator=(MyString&& other) noexcept {
            if (this != &other) {
                delete[] str;
                str = other.str;
                other.str = nullptr;
            }
            return *this;
        }
    
        // + 연산자 오버로딩 (문자열 연결)
        MyString operator+(const MyString& other) const {
            size_t len1 = str ? strlen(str) : 0;
            size_t len2 = other.str ? strlen(other.str) : 0;
            char* result = new char[len1 + len2 + 1];
    
            if (str) strcpy_s(result, len1 + 1, str);
            if (other.str) strcpy_s(result + len1, len2 + 1, other.str);
    
            MyString newString(result);
            delete[] result;
            return newString;
        }
    
        // == 연산자 오버로딩 (문자열 비교)
        bool operator==(const MyString& other) const {
            if (!str || !other.str) return str == other.str;
            return strcmp(str, other.str) == 0;
        }
    
        // != 연산자 오버로딩 (문자열 비교)
        bool operator!=(const MyString& other) const {
            return !(*this == other);
        }
    
        // << 연산자 오버로딩 (출력)
        friend std::ostream& operator<<(std::ostream& os, const MyString& s) {
            if (s.str) os << s.str;
            return os;
        }
    
        // 문자열 반환 (c_str() 함수)
        const char* c_str() const {
            return str;
        }
    };
    
    int main() {
        MyString s1("Hello");
        MyString s2("World");
    
        MyString s3 = s1 + " " + s2; // + 연산자 오버로딩 호출
    
        std::cout << "s1: " << s1 << std::endl; // << 연산자 오버로딩 호출
        std::cout << "s2: " << s2 << std::endl; // << 연산자 오버로딩 호출
        std::cout << "s3: " << s3 << std::endl; // << 연산자 오버로딩 호출
    
        if (s1 == s2) { // == 연산자 오버로딩 호출
            std::cout << "s1 and s2 are equal" << std::endl;
        } else {
            std::cout << "s1 and s2 are not equal" << std::endl;
        }
    
        if (s1 != s2) { // != 연산자 오버로딩 호출
            std::cout << "s1 and s2 are not equal" << std::endl;
        } else {
            std::cout << "s1 and s2 are equal" << std::endl;
        }
    
        return 0;
    }

    연산자 오버로딩 설명

    1. + 연산자:
      • MyString 객체를 연결하여 새로운 MyString 객체를 반환합니다.
      • 두 문자열의 길이를 계산하고, 동적 배열을 할당하여 두 문자열을 복사한 후 연결합니다.
    2. == 연산자:
      • MyString 객체가 같은 문자열을 가지고 있는지 비교합니다.
      • 문자열이 모두 존재할 경우 strcmp 함수를 사용하여 비교합니다.
    3. != 연산자:
      • MyString 객체가 다른 문자열을 가지고 있는지 비교합니다.
      • == 연산자를 사용하여 결과를 반전시킵니다.
    4. << 연산자:
      • MyString 객체를 std::ostream에 출력할 수 있도록 합니다.
      • friend 함수로 선언되어 MyString 클래스의 private 멤버에 접근할 수 있습니다.

    연산자 오버로딩의 장점

    1. 가독성 향상: 클래스 객체 간의 연산을 기본 타입처럼 사용할 수 있어 코드가 직관적이고 읽기 쉬워집니다.
    2. 편의성 제공: 사용자 정의 타입에 대해 익숙한 연산자를 사용할 수 있어 코드 작성이 간편해집니다.
    3. 일관성 유지: 클래스의 동작을 일관되게 유지할 수 있으며, 다른 개발자가 클래스를 사용할 때도 친숙하게 느낄 수 있습니다.

    결론

    연산자 다중 정의는 C++의 강력한 기능 중 하나로, 사용자 정의 타입을 더욱 직관적이고 편리하게 사용할 수 있게 합니다. 이를 통해 코드의 가독성과 유지 보수성을 높일 수 있습니다. MyString 클래스 예제는 문자열을 다루는 클래스에서 다양한 연산자를 오버로딩하여 사용하는 방법을 잘 보여줍니다.

    728x90
    반응형

    '프로그래밍 > C & C++' 카테고리의 다른 글

    [C++] History (C++11~20)  (3) 2024.07.15
    [C++] L-Value & R-Value  (0) 2024.07.15
    [C++] Explicit  (0) 2024.07.08
    [C++] 대입연산자  (0) 2024.07.08
    [C++] 댕글링 포인터 (Dangling Pointer)  (0) 2024.07.07
Designed by Tistory.