C++Stringクラスと濃淡コピー


C++Stringクラスと濃淡コピー
OJでよく使用されるStringクラスインタフェース
getline(cin,string)は標準入力からstring find/rfind()に文字列が初めて現れる下付きサブ列c.str()を返してstringクラスパッケージのchar*を手に入れ、strcpyと組み合わせて使用する
浅いコピー
コンパイラのデフォルトで合成されたコピー構造と付与演算子は、コンパイラの観点から現在のオブジェクトにそのままコピーされることを意味する浅いコピーです.問題の原因:複数のオブジェクトが同じ空間を使用し、解放された空間が他のオブジェクトにアクセスされるとプログラムがクラッシュします.
以下では、オブジェクトの動的購買依頼のリソースも現在のオブジェクトにコピーする深いコピーバージョンを実装します.
class String {
public:
    String(const char* str = "")
    {
        if (NULL == str) {
            str = "";
        }
        _pStr = new char[strlen(str) + 1];
        strcpy(_pStr, str);
    }

    String(int len, char c = 0)
        :_pStr(new char[len + 1]) {
        fill(_pStr, _pStr + len, c);
        _pStr[len] = '\0';
    }
    //     
    String(const String& s)
        :_pStr(new char[strlen(s._pStr) + 1])
    {
        strcpy(_pStr, s._pStr);
    }

    String& operator=(String s) {
        swap(_pStr, s._pStr);
        return *this;
    }
    //      
    //String& operator=(const String& s)
    //{
    //  if (this != &s) {
    //      char* pStr = new char[strlen(s._pStr) + 1];
    //      strcpy(pStr, s._pStr);
    //      delete[] _pStr;
    //      _pStr = pStr;
    //  }
    //  return *this;
    //}
    ~String()
    {
        if (_pStr) {
            delete[] _pStr;
        }
    }
    String operator+(const String& s) {
        int len = strlen(s._pStr) + strlen(_pStr) + 1;
        String tmp(len);//      
        strcpy(tmp._pStr, _pStr);
        strcat(tmp._pStr, s._pStr);
        return tmp;
    }

    //String operator+(String s) {
    //  int len = size() + s.size() + 1;
    //  char* tmp = new char[len];
    //  strcpy(tmp, _pStr);
    //  strcat(tmp, s._pStr);
    //  swap(tmp, s._pStr);
    //  delete[] tmp;
    //  return s;
    //}
    bool operator==(const String &str)const
    {
        if (strcmp(_pStr, str._pStr) == 0) {
            return true;
        }
        return false;
    }

    size_t size() const
    {
        return strlen(_pStr);
    }

    const char* c_str() const
    {
        return _pStr;
    }
    //  position       len           
    String& sub_str(int position, int len) {
        if (position<0 || position >= size() || len<0 || len >size()) //     ,      
        {
        }
        else
        {
            if (position + len - 1 >= size())            //        
                len = size() - position;
            for (int i = 0, j = position; i'\0';
        }
        return *this;
    }
private:
    char* _pStr;
};
/*    */
    //              ,     _str         
    String(const String& s)
    {
        String strTmp(s._pStr);
        swap(_pStr, strTmp._pStr);
    }

浅いコピーの参照カウント版
浅いコピーのリソース管理の問題を解決するために,参照カウントを提案し,各オブジェクトにリソースが参照された回数を保存し,オブジェクトが解析されたときに参照カウント>0であることが判明すれば,リソースを解放する必要はない.
具体的な実装:
構想1:静的メンバー変数でカウントを保存し、クラスオブジェクトすべてにアクセスできるようにします.エラー!オブジェクトがコピー構造を呼び出さず、通常のコンストラクション関数である場合、そのカウントと他のリソースを参照するオブジェクトは二義性をもたらします.
構想2:オブジェクトごとに1つのカウントポインタを保存し、各空間に1つのカウントを割り当て、構造をコピーするときにポインタをコピーし、構造を分析するとき-自分のポインタの参照カウント.できる!
構想3:文字列空間の前に4バイトをカウントするために予め割り当てておき、以下はこの実現に基づいている.
class String {
public:

    String(const char* str = "")
    {
        if (NULL == str) {
            str = "";
        }
        _pStr = new char[strlen(str) + 1 + 4];
        _pStr += 4;
        get_ref_count() = 1;
        strcpy(_pStr, str);
    }

    String(const String& s)
        :_pStr(s._pStr)
    {
        ++get_ref_count();
    }
    String& operator=(const String& s) {
        if (this != &s) {
            _Release();
            _pStr = s._pStr;
            ++get_ref_count();
        }
        return *this;
    }
    void _Release()
    {
        --get_ref_count();
        if (get_ref_count() == 0 && _pStr) {
            delete[] (_pStr-4);
        }
    }
    ~String()
    {
        if (_pStr) {
            _Release();
        }
    }
    int& get_ref_count()
    {
        return *(int*)(_pStr - 4);
    }
    //       ,    
    char& operator[](size_t index)
    {
        //              ,          
        if (get_ref_count() > 1) {
            --get_ref_count();
            String strTmp(_pStr);
            _pStr = NULL;
            swap(_pStr, strTmp._pStr);
        }
        return _pStr[index];
    }
    //[]   
    const char& operator[](size_t index)const
    {
        return _pStr[index];
    }
private:
    char* _pStr;
};

参照カウントが現れるため、カウントとリソースの操作が原子的であり、正規条件が存在することを保証する必要があります.だからマルチスレッドではこのクラスはまだ完備していません.
テスト例:
String s("abc");
String s2(s);
String s3("lll");
s3 = s;
s3[0] = 'w';//    
String s4("444");
String s5(s4);
s5 = s;