객체지향프로그래밍의 시작 -3

·2022년 5월 23일
0

cpp_study

목록 보기
6/25

내가 만들어보는 문자열 클래스

  1. 문자로부터의 문자열 생성, C 문자열 (char *)로부터의 생성
  2. 문자열 길이를 구하는 함수
  3. 문자열 뒤에 다른 문자열 붙이기
  4. 문자열 내에 포함되어 있는 문자열 구하기
  5. 문자열이 같은 지 비교
  6. 문자열 크기 비교(사전순)

문자열 클래스를 만들자

MyString이라는 우리만의 문자열 클래스를 만들어 보자.

우선 필요한 멤버 변수를 명시하자면,
1. 문자열 데이터가 저장된 공간을 가리키는 포인터
2. 문자열 데이터의 길이

가 된다.

이때 문자열은 개행문자를 포함하지 않는 것으로 저장하는 걸로 하자.

1. 문자로부터의 문자열 생성, C문자열(char*)로부터의 생성

특이점: 생성자로 받으면 무조건 해당하는 문자열의 주소값을 그냥 복사하는 게 아니라,
deep copy 형식으로 복사한다.

// Example program
#include <iostream>
#include <string.h>
using namespace std;

class MyString{
    
    char * string_content;
    int string_length;
    
public:
    
    MyString(char c);
    MyString(const char* str);
    MyString(const MyString& str);
    
    ~MyString();
    void print() const;
    void println() const;
};

MyString::MyString(char c){
    string_content = new char[1];
    string_content[0] = c;
    string_length = 1;
}

MyString::MyString(const char* str){
    string_length = strlen(str);
    string_content = new char[string_length];
    
    for (int i = 0; i != string_length; i++)
        string_content[i] = str[i];
        
}

MyString::MyString(const MyString& str){
    string_length = str.string_length;
    for (int i = 0; i != string_length; i++){
        string_content[i] = str.string_content[i];
    }
}

MyString::~MyString(){
    delete[] string_content;
}

void MyString::print() const{
    for(int i=0; i<string_length; i++){
        cout << string_content[i];
    }
    
}

void MyString::println() const {
    print();
    cout << endl;
}

int main()
{
    MyString str1("Hello World!");
    MyString str2(str1);
    
    str1.println();
    str2.println();
}

이때, 기존과는 달리 string_length로 문자열의 길이를 상수 시간내에 확인할 수 있다.
int MyString::length() const { return string_length; }

내부 멤버 변수의 값을 바꾸지 않는다면 함수를 꼭 상수로 정의하자

번외) cout과 endl 그리고 개행문자 '\n'

endl은 개행문자 '\n'에다가 flush를 더한 것과 동일하다고 볼 수 있다.
즉, cout << endl;cout << '\n' << flush 와 동일하다고 볼 수 있다.

여기서 flush란,

A buffer flush is the transfer of computer data from a temporary storage area to the computer’s permanent memory

으로, 버퍼에 있는 걸 비워내면서 동시에 그 내용물을 가져다가 쓰는 개념으로 이해하면 될 것 같다.

assign 함수

// Example program
#include <iostream>
#include <string.h>
using namespace std;

class MyString{
    
    char * string_content;
    int string_length;
    
public:
    
    MyString(char c);
    MyString(const char* str);
    MyString(const MyString& str);
    
    ~MyString();
    void print() const;
    void println() const;
    MyString& assign(const MyString& str);
    MyString& assign(const char* str);
};

MyString::MyString(char c){
    string_content = new char[1];
    string_content[0] = c;
    string_length = 1;
}

MyString::MyString(const char* str){
    string_length = strlen(str);
    string_content = new char[string_length];
    
    for (int i = 0; i != string_length; i++)
        string_content[i] = str[i];
}

MyString::MyString(const MyString& str){
    string_length = str.string_length;
    for (int i = 0; i != string_length; i++){
        string_content[i] = str.string_content[i];
    }
}

MyString::~MyString(){
    delete[] string_content;
}

void MyString::print() const{
    for(int i=0; i<string_length; i++){
        cout << string_content[i];
    }
    
}

void MyString::println() const {
    print();
    cout << endl;
}

// assign 함수 추가한 부분 1
MyString& MyString::assign(const MyString& str){
    if (str.string_length > string_length){
        delete[] string_content;
        string_content = new char[str.string_length];
    }
    
    for (int i=0; i!=str.string_length; i++){
        string_content[i] = str.string_content[i];
    }
    
    string_length = str.string_length;
    
    return *this;
}

// assign 함수 추가한 부분 2
MyString& MyString::assign(const char* str){
    int str_length = strlen(str);
    if (str_length > string_length){
        delete[] string_content;
        
        string_content = new char[str_length];
    }
    for (int i=0; i!= str_length; i++){
        string_content[i] = str[i];
    }
    
    string_length = str_length;
    return *this;
}

int main()
{
    MyString str1("Hello World!");
    MyString str2(str1);
    
    str1.println();
    str2.println();
}

만약, 긴 문자열 할당 -1-> 짧은 문자열 할당 -2-> 긴 문자열 할당
2번에서 assign 함수는 문자열에 짧은 문자열을 위한 작은 크기에 공간만이 할당되어 있다고 생각해,
기존 메모리를 해제하고(사실 이미 충분히 많은 메모리를 가지고 있음) 다시 많은 양의 메모리를 할당하는,
매우 비효율적인 작업을 하게 됨.

비효율을 막기 위해서는 얼마나 맣은 공간이 할당되어 있는 지 알 수 있는 정보를 따로 보관하는 것이 좋음.
-> memory_capacity 추가하기

memory_capacity 추가하고 at 추가하기

  • 문자열의 길이를 리턴하는 함수(length),
  • 문자열의 대입 함수(assign),
  • 문자열 메모리 할당 함수(reserve) 및 현재 할당된 크기를 알아오는 함수(capacity),
  • 특정 위치의 문자를 리턴하는 함수(at)
// Example program
#include <iostream>
#include <string.h>
using namespace std;

class MyString{
    
    char * string_content;
    int string_length;
    int memory_capacity;
    
public:
    
    MyString(char c);
    MyString(const char* str);
    MyString(const MyString& str);
    
    ~MyString();
    void print() const;
    void println() const;
    MyString& assign(const MyString& str);
    MyString& assign(const char* str);
    
    int capacity() const;
    void reserve(int size);
    int length() const;
    char at(int i) const;
};

MyString::MyString(char c){
    string_content = new char[1];
    string_content[0] = c;
    string_length = 1;
}

MyString::MyString(const char* str){
    string_length = strlen(str);
    string_content = new char[string_length];
    
    for (int i = 0; i != string_length; i++)
        string_content[i] = str[i];
}

MyString::MyString(const MyString& str){
    string_length = str.string_length;
    for (int i = 0; i != string_length; i++){
        string_content[i] = str.string_content[i];
    }
}

MyString::~MyString(){
    delete[] string_content;
}

void MyString::print() const{
    for(int i=0; i<string_length; i++){
        cout << string_content[i];
    }
    
}

void MyString::println() const {
    print();
    cout << endl;
}

MyString& MyString::assign(const MyString& str){
    if (str.string_length > memory_capacity){
        delete[] string_content;
        string_content = new char[str.string_length];
        memory_capacity = str.string_length;
    }
    
    for (int i=0; i!=str.string_length; i++){
        string_content[i] = str.string_content[i];
    }
    
    string_length = str.string_length;
    
    return *this;
}

MyString& MyString::assign(const char* str){
    int str_length = strlen(str);
    if (str_length > string_length){
        delete[] string_content;
        
        string_content = new char[str_length];
        memory_capacity = str_length;
    }
    for (int i=0; i!= str_length; i++){
        string_content[i] = str[i];
    }
    
    string_length = str_length;
    return *this;
}

void MyString::reserve(int size){
    if (size > memory_capacity){
        char* prev_string_content = string_content;
        
        string_content = new char[size];
        memory_capacity = size;
        
        for(int i=0; i!=string_length; i++){
            string_content[i] = prev_string_content[i];
        }
        
        delete[] prev_string_content;
    }
}

char MyString::at(int i) const{
    if (i>= string_length || i<0)
        return NULL;
    else
        return string_content[i];
}

int MyString::length() const{
    return string_length;
}

int MyString::capacity() const{
    return memory_capacity;
}

int main()
{
    MyString str1("very very very long string"); str1.reserve(30);
    std::cout << "Capacity : " << str1.capacity() << std::endl;
    std::cout << "String length : " << str1.length() << std::endl;
    str1.println();
}

문자열 삽입하기(insert)

i의 위치 바로 앞에 삽입한다고 하자.
아래 코드 참고.

// Example program
#include <iostream>
#include <string.h>
using namespace std;

class MyString{
    
    char * string_content;
    int string_length;
    int memory_capacity;
    
public:
	// 생략
};
MyString& MyString::insert(int loc, const MyString& str){
    if (loc < 0 || loc > string_length) return *this;
    
    if (string_length + str.string_length > memory_capacity){
        memory_capacity = string_length + str.string_length;
        char * prev_string_content = string_content;
        
        string_content = new char[memory_capacity];
        
        int i;
        for (i=0; i<loc; i++){
            string_content[i] = prev_string_content[i];
        }
        for (int j = 0; j<str.string_length+loc; j++){
            string_content[j] = str.string_content[j];
        }
        for (; i<string_length; i++){
            string_content[str.string_length + i] = prev_string_content[i];
        }
        
        delete[] prev_string_content;
        
        string_length = string_length + str.string_length;
        return *this;
    }
    
    for (int i = string_length -1; i>=loc; i--){
        string_content[i+str.string_length] = string_content[i];
    }
    
    for (int i = 0; i< str.string_length; i++){
        string_content[i+ loc] = str.string_content[i];
    }
    
    string_length = string_length + str.string_length;
    return *this;
    
}

MyString& MyString::insert(int loc, const char* str){
    MyString temp(str);
    return insert(loc, temp);
}

MyString& MyString::insert(int loc, char c){
    MyString temp(c);
    return insert(loc, temp);
}


int main()
{
    MyString str1("very long string");
    MyString str2("<some string inserted between>"); 
    str1.reserve(50);
    
    std::cout << "Capacity : " << str1.capacity() << std::endl;
    std::cout << "String length : " << str1.length() << std::endl;
    
    str1.println();
    str1.insert(5, str2);
    str1.println();
}

동적으로 할당되는 메모리 처리

C++에서는 동적으로 할당되는 메모리를 처리하는데 매우 빈번하게 사용되는 기법은 다음과 같다.

  • IF 새로 할당할 메모리 크기 < 현재의 memory_capacity*2
    미리 현재 memory_capacity 를 할당해 버린다(예약)
  • ELSE
    예약을 사용하지 않고 필요한 만큼만 할당함

erase 함수

생략, 비슷하게 진행한다.

find 함수

// Example program
#include <iostream>
#include <string.h>
using namespace std;

class MyString{
    
    char * string_content;
    int string_length;
    int memory_capacity;
    
public:
    
    MyString(char c);
    MyString(const char* str);
    MyString(const MyString& str);
    
    ~MyString();
    void print() const;
    void println() const;
    MyString& assign(const MyString& str);
    MyString& assign(const char* str);
    
    int capacity() const;
    void reserve(int size);
    int length() const;
    char at(int i) const;
    
    MyString& insert(int loc, const MyString& str);
    MyString& insert(int loc, const char* str);
    MyString& insert(int loc, char c);
    
    int find(int find_from, const MyString& str) const; 
    int find(int find_from, const char* str) const;
    int find(int find_from, char c) const;

};

MyString::MyString(char c){
    string_content = new char[1];
    string_content[0] = c;
    string_length = 1;
}

MyString::MyString(const char* str){
    string_length = strlen(str);
    string_content = new char[string_length];
    
    for (int i = 0; i != string_length; i++)
        string_content[i] = str[i];
}

MyString::MyString(const MyString& str){
    string_length = str.string_length;
    for (int i = 0; i != string_length; i++){
        string_content[i] = str.string_content[i];
    }
}

MyString::~MyString(){
    delete[] string_content;
}

void MyString::print() const{
    for(int i=0; i<string_length; i++){
        cout << string_content[i];
    }
    
}

void MyString::println() const {
    print();
    cout << endl;
}

MyString& MyString::assign(const MyString& str){
    if (str.string_length > memory_capacity){
        delete[] string_content;
        string_content = new char[str.string_length];
        memory_capacity = str.string_length;
    }
    
    for (int i=0; i!=str.string_length; i++){
        string_content[i] = str.string_content[i];
    }
    
    string_length = str.string_length;
    
    return *this;
}

MyString& MyString::assign(const char* str){
    int str_length = strlen(str);
    if (str_length > string_length){
        delete[] string_content;
        
        string_content = new char[str_length];
        memory_capacity = str_length;
    }
    for (int i=0; i!= str_length; i++){
        string_content[i] = str[i];
    }
    
    string_length = str_length;
    return *this;
}

void MyString::reserve(int size){
    if (size > memory_capacity){
        char* prev_string_content = string_content;
        
        string_content = new char[size];
        memory_capacity = size;
        
        for(int i=0; i!=string_length; i++){
            string_content[i] = prev_string_content[i];
        }
        
        delete[] prev_string_content;
    }
}

char MyString::at(int i) const{
    if (i>= string_length || i<0)
        return NULL;
    else
        return string_content[i];
}

int MyString::length() const{
    return string_length;
}

int MyString::capacity() const{
    return memory_capacity;
}

MyString& MyString::insert(int loc, const MyString& str){
    if (loc < 0 || loc > string_length) return *this;
    
    if (string_length + str.string_length > memory_capacity){
        memory_capacity = string_length + str.string_length;
        char * prev_string_content = string_content;
        
        string_content = new char[memory_capacity];
        
        int i;
        for (i=0; i<loc; i++){
            string_content[i] = prev_string_content[i];
        }
        for (int j = 0; j<str.string_length+loc; j++){
            string_content[j] = str.string_content[j];
        }
        for (; i<string_length; i++){
            string_content[str.string_length + i] = prev_string_content[i];
        }
        
        delete[] prev_string_content;
        
        string_length = string_length + str.string_length;
        return *this;
    }
    
    for (int i = string_length -1; i>=loc; i--){
        string_content[i+str.string_length] = string_content[i];
    }
    
    for (int i = 0; i< str.string_length; i++){
        string_content[i+ loc] = str.string_content[i];
    }
    
    string_length = string_length + str.string_length;
    return *this;
    
}

MyString& MyString::insert(int loc, const char* str){
    MyString temp(str);
    return insert(loc, temp);
}

MyString& MyString::insert(int loc, char c){
    MyString temp(c);
    return insert(loc, temp);
}
int MyString::find(int find_from, const MyString& str) const {
    int i, j;
    if (str.string_length == 0) return -1;
    for (i = find_from; i <= string_length - str.string_length; i++) {
        for (j = 0; j < str.string_length; j++) {
        if (string_content[i + j] != str.string_content[j]) break;
        }
        if (j == str.string_length) return i; 
    }
    return -1; 
    // 찾지 못했음 
}

int MyString::find(int find_from, const char* str) const { 
        MyString temp(str);
        return find(find_from, temp);
    }
    
int MyString::find(int find_from, char c) const {
  MyString temp(c);
    return find(find_from, temp); 
}

int main()
{
    MyString str1("this is a very very long string");
    std::cout << "Location of first <very> in the string : " << str1.find(0, "very")<< std::endl;
    std::cout << "Location of second <very> in the string : " << str1.find(str1.find(0, "very") + 1, "very") << std::endl;
}

크기 비교 함수 compare

compare 역시 쉽게 구현할 수 있다. 생략.

생각해보기

여러 가지 검색 알고리즘들을 이용해 find 함수를 만들어보자.
어떤 알고리즘의 경우 미리 계산된 테이블이 필요한데, 이러한 정보들 역시 class 변수로 처리해도 된다.

profile
이것저것 개발하는 것 좋아하지만 서버 개발이 제일 좋더라구요..

0개의 댓글