[C++] 12. string 클래스의 디자인

kkado·2023년 10월 17일
0

열혈 C++

목록 보기
12/16
post-thumbnail

💬 윤성우 님의 <열혈 C++ 프로그래밍> 책을 혼자 공부하며 배운 내용을 정리합니다. 글의 모든 내용은 책에서 발췌하였습니다.


표준 string 클래스

C++ 표준 라이브러리에는 string 이라는 이름의 클래스가 정의되어 있고, 그 이름처럼 문자열의 처리를 위해 정의된 클래스이다. 이 클래스를 사용하기 위해서는 헤더 파일 <string> 을 include해야 한다.

일단 바로 string 클래스의 예제부터 보자.

int main()
{
    string str1 = "I like ";
    string str2 = "string class";
    string str3 = str1 + str2;

    cout << str1 << "\n";
    cout << str2 << "\n";
    cout << str3 << "\n";

    str1 += str2;

    if (str1 == str3)
        cout << "동일 문자열\n";
    else
        cout << "동일하지 않은 문자열\n";

    string str4;
    cout << "문자열 입력 : ";
    cin >> str4;
    cout << "입력한 문자열 : " << str4 << "\n";
}
I like 
string class
I like string class
동일 문자열
문자열 입력 :  seunggi
입력한 문자열 : seunggi

위 코드에서 볼 수 있다시피 operator+ 연산자 오버로딩으로 인해 string 객체간 덧셈 연산이 가능하고, 그 결과로 문자열 두 개를 이어붙인 객체가 만들어진다.

또한 << 연산자와 >> 연산자도 오버로딩이 되어 있어서 문자열을 입력하고 출력하는 것도 가능하다.


문자열 처리 클래스

이제 string 클래스를 대체할 수 있는 String 클래스를 직접 만들어보려고 한다. 먼저 string 클래스를 대체하기 위해서 필요한 요구사항을 정리해 보겠다.

  1. 문자열을 인자로 받는 생성자의 정의
    string str1 = "I like "; 와 같이 string 객체를 만들었으며, 이는 string str1("I like "); 와 같은 문장이다.

  2. 생성자, 소멸자, 복사 생성자, 대입 연산자의 정의
    저장하고자 하는 문자열의 길이가 일정하지 않으므로 메모리를 생성자 내에서 동적할당 해야 하고, 이어서 소멸자를 정의해야 하며, 깊은 복사를 하기 위해 복사 생성자와 대입 연산자까지 함께 정의해야 한다.

  3. 결합된 문자열로 초기화된 객체를 반환하는 +의 오버로딩
    두 문자열을 합한 문자열의 주소 값을 반환할 수도 있고, 그 객체 자체를 반환할 수도 있겠으나 후자로 정의한다.

  4. 문자열을 덧붙일 수 있는 +=의 오버로딩
    앞서 살펴본 str1 += str2 와 같이 기존의 문자열에 새로운 문자열을 덧붙이기 위한 오버로딩이 필요하다.

  5. 내용비교의 == 오버로딩
    두 문자열이 같은지 여부를 확인하기 위해 == 연산자의 오버로딩이 필요하다.

  6. 콘솔 입출력이 가능하도록 << >> 의 오버로딩
    string 객체를 대상으로 콘솔에 출력 / 콘솔을 통한 입력도 가능해야 한다.

그리고 아래는 책에서 제시하는 예시 클래스이다.

class String
{
private:
    int len;
    char* str;

public:
    String()
    {
        len = 0;
        str = NULL;
    }
    String(const char* s)
    {
        len = strlen(s) + 1;
        str = new char[len];
        strcpy(str, s);
    }
    String(const String& s)
    {
        len = s.len;
        str = new char[len];
        strcpy(str, s.str);
    }

    ~String()
    {
        if(str != NULL)
            delete []str;
    }

    String& operator= (const String& s)
    {
        if (str != NULL)
            delete []str;

        len = s.len;
        str = new char[len];
        strcpy(str, s.str);
        return *this;
    }

    String& operator+(const String& s)
    {
        char* tmpStr = new char[len + strlen(str) - 1];
        strcpy(tmpStr, str);
        strcat(tmpStr, s.str);

        String tmp(tmpStr);
        delete tmpStr;
        return tmp;
    }

    String operator+=(const String& s)
    {
        len += (s.len - 1);
        char* tmpStr = new char[len];
        strcpy(tmpStr, str);
        strcat(tmpStr, s.str);

        if (str != NULL)
            delete []str;
        str = tmpStr;

        return *this;
    }

    bool operator==(const String& s)
    {
        return strcmp(str, s.str) ? false : true;
    }

    friend ostream& operator<<(ostream& os, const String& s);
    friend ostream& operator>>(ostream& os, const String& s);
};

ostream& operator<<(ostream& os, const String& s)
{
    os << s.str;
    return os;
}


istream& operator>>(istream& is, String& s)
{
    char str[100];
    is >> str;
    s = String(str);
    return is;
}

문자열 없이도 객체를 생성할 수 있게 빈 생성자를 만든 것, 깊은 복사가 가능하도록 동적 할당을 통해서 새로운 문자열을 만들고 있다는 것에 유의하자.

+ 연산자에서 새롭게 할당할 공간의 길이를 계산할 때 -1을 하는 이유는 멤버 변수 len에 저장된 문자열의 길이는 문자열의 맨 끝에 널 문자가 포함되어 1만큼 긴 값인데 이것이 두 번 더해졌기 때문이다.

또한 += 연산자에서는, 배열은 확장이 불가능하므로 덧붙일 문자열의 길이를 더한 만큼의 길이를 가지는 새로운 문자열을 만들어서 이를 이용해서 객체를 만든다.


profile
베이비 게임 개발자

0개의 댓글