データ構造--文字列クラスString(一)

9710 ワード

C言語では本当の意味での文字列はサポートされていません.Cでは文字配列に'0'を加えて文字列として結合し、strcpy、strcmpなどの関数のセットと協力して文字列の操作を実現します.C言語ではカスタムタイプはサポートされていないため、文字列タイプは取得できません.
CからC++への進化の過程でカスタムデータ型が導入されたため、C++ではクラスタイプによって文字列タイプの定義を完了することができる.
テンプレートライブラリで文字列クラスを実装する必要があるのはなぜですか?STL標準ライブラリにはcstringクラスがあり、QTにはqstringがあります.それは、文字列を処理することが多いため、私たちの多重化可能なテンプレートライブラリでは文字列クラスを提供する必要があります.
設計要件
テンプレートプログラミング.
最上位の親Objectから継承されます.
保護メンバー:char*ポインタm_str、記録文字列長のm_length変数、初期化m_strとm_lengthのinit関数、2つのlen長の文字列が等しいか否かを判断する関数equal.
共有メンバー:コンストラクション関数、オペレータリロード関数、機能関数、コンストラクション関数など.
注意事項:
1.クラスオブジェクトと文字列の間でシームレスに接続して相互運用できることを考慮する.
2、オペレータリロード関数がconstバージョンを実装する必要があるかどうかを考慮します.
3、Cの文字列操作関数を利用してstringクラスのメンバー関数を実現する.
stringクラス宣言
class String : public Object
{
protected:
    char* m_str;
    int m_length;
    void init(const char* s);
    bool equal(const char* l, const char* r, int len)const;

public:
    String();
    String(char c);
    String(const char* s);
    String(const String& s);

    int length() const;
    const char* Str() const;


    char& operator [](int i);//              ,  const    
    char operator [](int i)const;

    //                   
    bool StartWith(const char* s)const;//         
    bool StartWith(const String& s) const;
    bool EndOf(const char* s)const;
    bool EndOf(const String& s)const;

    //    
    String& insert(int i, const char* s);//    ,               `
    String& insert(int i, const String& s);

    //          
    String& trim();

    bool operator == (const String& s)const;
    bool operator == (const char* s)const;
    bool operator != (const String& s)const;
    bool operator != (const char* s)const;
    bool operator > (const String& s)const;
    bool operator > (const char* s)const;
    bool operator < (const String& s)const;
    bool operator < (const char* s)const;
    bool operator >= (const String& s)const;
    bool operator >= (const char* s)const;
    bool operator <= (const String& s)const;
    bool operator <= (const char* s)const;


    String operator + (const String& s)const;
    String operator + (const char* s)const;
    String& operator += (const String& s);//                   
    String& operator += (const  char* s);

    String& operator = (const String& s);
    String& operator = (const  char* s);
    String& operator = (const char c);


    ~String();



};

オペレータリロード関数
init関数:
void String::init(const char* s)
{
    m_str = strdup(s);//    s    
    if( m_str )
    {
        m_length = strlen((const char*)m_str);
    }
    else
    {
        THROW_EXCEPTION(NoEnoughMemoryException, "No memeory to create String object...");
    }
}
初期化が必要な文字列をヒープ空間にコピーし、保護メンバー変数m_に値を付与するstr、strdup関数を呼び出します.失敗した場合は例外を放出します.
初期化文字列の長さを取得します.
コンストラクタ
String::String()
{
    init("");

}
String::String(char c)
{
    char s[] = {c, '\0'};//       
    init(s);
}
String::String(const char* s)
{
    init( s ? s : "");//         
}
String::String(const String& s)
{
    init(s.m_str);
}

Init関数を呼び出して構造関数を実現することにより,設計の多重性を体現した.
Str関数
const char* String::Str()const
{
    return this->m_str;
}
は、クラスオブジェクトと文字列との相互運用を実現する.
オペレータリロード関数
各タイプのオペレータリロード関数には、const char*とconst string&タイプの2つのバージョンが必要です.char*とクラスオブジェクトを満たす必要があるため
// ==       
bool String::operator == (const String& s)const
{
    return strcmp(this->m_str,s.m_str) == 0;
}
bool String::operator == (const char* s)const
{
    return strcmp(this->m_str,s ? s : "") == 0;
}

// !=        
bool String::operator != (const String& s)const
{
    return !(this->m_str == s.m_str);
}
bool String::operator != (const char* s)const
{
    return !(this->m_str == s ? s : "");
}

ここでは、比較オペレータのリロードの一部のみが実現され、他の比較オペレータのリロードの原理は一致している.C言語のstrcmp関数を利用します.
+、+=オペレータリロード関数
// +        
String String::operator + (const char* s)const//const                                    
{
    String ret;

    int len = m_length + strlen( s ? s : "");
    char* str = reinterpret_cast(malloc(len + 1));
    if(str)
    {
        strcpy(str,m_str);
        strcat(str,s ? s : "");

        free(m_str);

        ret.m_str = str;
        ret.m_length = len;
    }
    else
    {
        THROW_EXCEPTION(NoEnoughMemoryException, "No memeory to mlalloc...");
    }
    return ret;
}
String String::operator + (const String& s)const
{
    return (*this + s.m_str);
}

// +=        
String& String::operator += (const String& s)
{
    return (*this = *this + s);
}
String& String::operator += (const  char* s)
{
    return (*this = *this + s);
}

+オペレータをリロードするconst char*バージョン実装では、まず関数の戻り値タイプがstringタイプです.なぜですか.+オペレータの元の意味で+操作後に結果を保存する必要はないので、ここでは一時的なオブジェクトを返します.関数体ではまず接合後の文字列長を取得し,その後スタック空間でその長さを保存できる文字列サイズの空間を申請し,C言語のstrncpyとstrcat関数を用いて2つの文字列を接続する.2つのバージョンを実装するのもconst char*とクラスオブジェクト間の相互運用のためです.
+=オペレータリロードインプリメンテーションでは、関数の戻り値はstring&タイプであり、オペレータの本来の意味に合致し、+後の結果を保存し、関数は+オペレータのリロード関数を呼び出した後、現在のオブジェクトのthisポインタに値を付与します.
=オペレータリロード
前述したように、割り当てオペレータが使用されている以上、割り当てオペレータの再ロードを実現する必要があります.
// =        
String& String::operator = (const String& s)
{
    return (*this = s.m_str);
}
String& String::operator = (const  char* s)
{
    if(m_str != s)//     
    {
        char* str = strdup(s ? s : "");//          
        if(str)
        {
            free(m_str);
            m_str = str;
            m_length = strlen(m_str);

        }
        else
        {
           THROW_EXCEPTION(NoEnoughMemoryException, "No memeory to get in heap...");
        }
    }
    return *this;
}
String& String::operator = (const char c)
{
    char str[] = {c, '\0'};

    return (*this = str);
}

const char*バージョンを実装することで、他のコード多重化が可能になります.
配列アクセスオペレータ
// []     
char& String::operator [](int i)//      ,              
{
    if( (i >= 0) && (i < m_length) )
    {
        return m_str[i];
    }
    else
    {
        THROW_EXCEPTION(IndexOutOfBoundsException,"para i Index Out to use []...");

    }
}
char String::operator [](int i)const//   const  ,      
{
    return (const_cast(*this)[i]);//  
}

きのうかんすう
2つの等長文字列が等しいかどうかを判断します:equal
bool String::equal(const char* l, const char* r, int len)const
{
    bool ret = true;

    for(int i = 0; i < len && ret; i++)//                 
    {
        ret = ret && (l[i] == r[i]);
    }
    return ret;
}

指定した文字列で開始するかどうか:Start With
bool String::StartWith(const char* s)const//         
{
    bool ret = (s != NULL);
    if( ret )
    {
        int len = strlen(s);
        ret = (len <= m_length) && (equal(m_str, s, len));//      
    }

    return ret;
}
bool String::StartWith(const String& s) const
{
    return (StartWith(s.m_str));
}

2つのバージョンで、相互運用が可能です.
指定した文字列で終わるかどうか:EndOf
bool String::EndOf(const char* s)const
{
    bool ret = (s != NULL);
    if( ret )
    {
        int len = strlen(s);
        char* start = m_str + (m_length - len);//     len     
        ret = (len <= m_length) && (equal(start, s, len));
    }

    return ret;
}
bool String::EndOf(const String& s)const
{
    return (EndOf(s.m_str));
}
の2つのバージョンで、相互運用が可能です.
挿入関数そうにゅうかんすう:insert insert insert insert
String& String::insert(int i, const char* s)//    ,              
{
    if( (i >= 0) && (i <= m_length))
    {
        if( (s != NULL) && (s[0] != '\0') )
        {
            int len = strlen(s);
            char* str = reinterpret_cast(malloc(m_length + len + 1));
            if(str)
            {
                strncpy(str,m_str,i);
                strncpy(str + i, s, len );
                strncpy(str + i + len, m_str+i, m_length - i);

                str[m_length + len] = '\0';

                free(m_str);
                m_str = str;
                m_length = m_length + len;
            }
            else
            {
                 THROW_EXCEPTION(NoEnoughMemoryException,"No memory to insert ...");
            }
        }

    }
    else
    {
        THROW_EXCEPTION(IndexOutOfBoundsException,"para i Index Out ...");
    }

    return *this;
}
String& String::insert(int i, const String& s)
{
    return insert(i,s.m_str);
}

挿入は4つのステップに分かれます.挿入後の文字列サイズの空間を作成し、挿入する位置の前の文字列を新しい空間にコピーし、挿入する文字列を新しい空間の最後に接続し、最後に挿入する位置の後の文字列を新しい空間に接続します.
戻り値string&は、チェーン操作、すなわち(クラスオブジェクト)を実現することを目的とする.(メンバー関数1).(メンバー関数2).(...)
2つのバージョンは、相互運用を実現するためです.
文字列の先頭と末尾の空白部分を削除する関数:trim
//        
String& String::trim()//              
{
    int begin = 0;
    int end = m_length - 1;

    while(m_str[begin] == ' ') begin++;//           
    while(m_str[end]   == ' ') end--;

    if(begin == 0)//       
    {
        m_str[end + 1] = '\0';
        m_length = end + 1;
    }
    else
    {

        for(int i = 0, j = begin; j <= end; i++, j++ )//  
        {
            m_str[i] = m_str[j];
        }

        m_str[end - begin + 1] = '\0';
        m_length = end - begin + 1;
    }

    return *this;
}

本質は、中間部分の文字列をこの空間の最初の位置に移動することです.
ここでディテドン先生に感謝します.