2014-01-28 79 views
0

我想在C++中實現一個基本的string類。但是,我卡在append()函數。它希望它是Hello World,但結果Hello ÍWorlýýýý««««««««þC++字符串執行錯誤

#define __START namespace lib{ 
#define __END } 

__START 
class string 
{ 
public: 
    string(const char* s) 
    { 
     _str = s; 
    } 
    const char operator[](int position) 
    { 
     return _str[position]; 
    } 
    void operator=(string s) 
    { 
     _str = s.c_str(); 
    } 
    void append(string s) 
    { 
     size_t nlength = (length() + s.length()); 
     char* nstr = new char[nlength]; 
     for (int i = 0; i < length(); ++i) 
     { 
      nstr[i] = _str[i]; 
     } 
     for (int i = length() + 1; i < nlength; ++i) 
     { 
      nstr[i] = s[(i - length() - 1)]; 
     } 
     _str = const_cast<const char*>(nstr); 
    } 
    void operator+=(string s) 
    { 
     append(s); 
    } 
    const char* c_str() 
    { 
     return _str; 
    } 
    size_t length() 
    { 
     return strlen(_str); 
    } 
    std::string str() 
    { 
     return _str; 
    } 
private: 
    const char* _str; 
}; 
__END 

int main() 
{ 
    lib::string s = "Hello "; 
    s.append("World"); // s += "World"; 
    std::cout << s.c_str(); 
    getchar(); 
} 
+3

通過一切手段,爲您的字符串類使用不同的名稱。 'string'與'std :: string'衝突。當然你可以有'yournamespace :: string',但最好避免混淆。 –

+4

@DanielDaranas我會打賭1000美元,OP是'使用命名空間標準;'無論如何。 – 2014-01-28 18:05:47

+4

您需要分配nlength + 1,然後將'\ 0'添加到字符串末尾 –

回答

1

存在噸的問題與您的代碼,所以讓我們一步一步做。

首先,您不需要命名空間的預處理器魔術,而只需要基本的namespace mynamespace{}

其次,創建一個basic_string類是理想的,因此它可以用於不同的字符類型。 char/wchar_t,e.t.c.

1)private: const char *_str;

問題與你的類。指針將被修改,所以const是無用的。 2)沒有typedef s。如果您試圖重新實現STL類,則需要它們。 (在例子中解釋)

3)使用allocator。這樣你就可以construct,destroy元素,allocatedeallocate內存。你肯定會以這種方式讓你的記憶更安全。

4)字符串必須爲空終止。這意味着最後需要額外的'\0',這意味着您必須爲此分配額外的字節。字符串以null結尾,因爲它是告訴代碼停止讀取字符串的一種方式。

5)您正在分配給尚未分配的字符串。 _str = s.c_str();可能很容易崩潰,這取決於您的編譯器,因爲您正在寫入未分配的內存。

6)使用const引用,而不是您的參數的正常類型(string = const string &)。您還需要這個爲您的複製構造函數basic_string(const _Myt &)

我仍可能沒有突出所有的問題

basic_string

template < typename _Elem, typename _Traits = std::char_traits<_Elem>, typename _Alloc = std::allocator<_Elem> > class basic_string 
{ 
public: 
    typedef basic_string<_Elem, _Traits, _Alloc> _Myt; 
    typedef _Elem value_type; 
    typedef _Traits traits_type; 
    typedef _Alloc allocator_type; 
    typedef value_type *pointer; 
    typedef const value_type *const_pointer; 
    typedef value_type *iterator; 
    typedef const value_type *const_iterator; 
    typedef value_type &reference; 
    typedef const value_type &const_reference; 
    typedef std::size_t size_type; 
    typedef std::ptrdiff_t difference_type; 

    basic_string() 
    { 
     __data = _Alloc().allocate(1); 
     _Alloc().construct(&__data[0], '\0'); 
    } 

    basic_string(const_pointer _Init) 
    { 
     int count = 0; 
     __data = _Alloc().allocate(_Traits::length(_Init) + 1); 
     for (const_iterator i = &_Init[0]; i != &_Init[_Traits::length(_Init)]; ++i, ++count) 
     { 
      _Alloc().construct(&__data[count], *i); 
     } 
     _Alloc().construct(&__data[_Traits::length(_Init)], '\0'); 
    } 

    basic_string(const _Myt &_Init) 
    { 
     if (this != &_Init) 
     { 
      int count = 0; 
      __data = _Alloc().allocate(_Traits::length(_Init.__data) + 1); 
      for (const_iterator i = &_Init.__data[0]; i != &_Init.__data[_Traits::length(_Init.__data)]; ++i, ++count) 
      { 
       _Alloc().construct(&__data[count], *i); 
      } 
      _Alloc().construct(&__data[_Traits::length(_Init.__data)], '\0'); 
     } 
     else 
     { 
      __data = _Alloc().allocate(1); 
      _Alloc().construct(&__data[0], '\0'); 
     } 
    } 

    ~basic_string() 
    { 
     if (__data) 
     { 
      size_type tmp = size(); 
      for (iterator i = begin(); i != end(); ++i) 
      { 
       _Alloc().destroy(i); 
      } 
      _Alloc().deallocate(__data, tmp); 
     } 
    } 

    _Myt &assign(const_pointer _Rhs) 
    { 
     int count = 0; 
     reserve(_Traits::length(_Rhs) + 1); 
     for (const_iterator i = &_Rhs[0]; i != &_Rhs[_Traits::length(_Rhs)]; ++i, ++count) 
     { 
      _Alloc().construct(&__data[count], *i); 
     } 
     _Alloc().construct(&__data[_Traits::length(_Rhs)], '\0'); 
     return *this; 
    } 

    _Myt &operator=(const_pointer _Rhs) 
    { 
     return assign(_Rhs); 
    } 

    _Myt &append(const_pointer _Rhs) 
    { 
     int count = size(); 
     reserve(size() + _Traits::length(_Rhs) + 1); 
     for (const_iterator i = &_Rhs[0]; i != &_Rhs[_Traits::length(_Rhs)]; ++i, ++count) 
     { 
      _Alloc().construct(&__data[count], *i); 
     } 
     _Alloc().construct(&__data[count], '\0'); 
     return *this; 
    } 

    _Myt &operator+=(const_pointer _Rhs) 
    { 
     return append(_Rhs); 
    } 

      iterator begin() 
    { 
     return &__data[0]; 
    } 

    iterator end() 
    { 
     return &__data[size()]; 
    } 

    size_type size() 
    { 
     return _Traits::length(__data); 
    } 

    _Myt &swap(basic_string<_Elem> &_Rhs) 
    { 
     std::swap(__data, _Rhs.__data); 
     return *this; 
    } 

    void reserve(size_type _Size) 
    { 
     int count = 0; 
     if (_Size < size()) 
     { 
      return; 
     } 
     pointer buf = _Alloc().allocate(_Size); 
     for (iterator i = begin(); i != end(); ++i, ++count) 
     { 
      _Alloc().construct(&buf[count], *i); 
     } 
     std::swap(__data, buf); 
     for (iterator i = &buf[0]; i != &buf[_Traits::length(buf)]; ++i) 
     { 
      _Alloc().destroy(i); 
     } 
     _Alloc().deallocate(buf, _Traits::length(buf)); 
    } 

    operator const_pointer() 
    { 
     return __data; 
    } 

    operator pointer() 
    { 
     return __data; 
    } 

    template < typename _Traits1, typename _Alloc1 > friend std::basic_ostream<_Elem> &operator<<(std::basic_ostream<_Elem> &_Stream, basic_string<_Elem, _Traits1, _Alloc1> &_Str) 
    { 
     return _Stream << _Str.c_str(); 
    } 

    const_pointer data() const 
    { 
     return __data; 
    } 

    const_pointer c_str() const 
    { 
     return __data; 
    } 
private: 
    pointer __data; 
}; 

typedef basic_string<char> string; 
typedef basic_string<wchar_t> wstring; 

當然,還有一個有點缺,但我敢肯定,你可以實現這一點在線幫助。

0
for (int i = length() + 1; i < nlength; ++i) 
{ 
    nstr[i] = s[(i - length() - 1)]; 
} 

你先拷貝字符最多length() - 1,然後在長啓動回()+ 1,所以你跳過一個字符,分配後可能有任何內容(除非事先做了一個memset)。

然後,您需要用空字符(\ 0)來終止您的字符串,以便在str()方法返回時構造的std::string知道何時停止讀取字符。

1

你在第二個循環中有一個錯誤的錯誤;第二個字符串需要被複制到length(),第一結束後立刻道:

for (int i = length(); i < nlength; ++i) 
{ 
    nstr[i] = s[i - length()]; 
} 

您還需要分配一個多字節把一個空終止的結束。

請注意,您不需要那個可怕的角色將const添加到指針,因爲這是一個非常安全的事情。 const_cast只需要刪除限定符。您可能還想修復內存泄漏問題,並且爲了獲得積分,請緩存該內存泄漏的長度,以便您不必在每次需要時都讀取整個字符串。

1

首先,你最關鍵的問題是在char* nstr = new char[nlength];

您必須將其更改爲char* nstr = new char[nlength+1];

然後,在功能append,兩個for循環後,設置nstr[nlength] = 0;

其次,爲了更好的性能(以及正確的編碼),你可能需要在下面的函數來改變string sconst string& s

void operator=(string s) 
void append(string s) 
void operator+=(string s) 
4

有很多的錯誤,不僅與追加

string(const char* s) 
{ 
    _str = s; 
} 

構造函數是不對的,你應該做的s副本,以釋放它以後,這種方式:

~string() 
{ 
    delete[] _str; // or free(_str) in case you use malloc/realloc, thanks Fred! 
} 

私有成員變量:

private: 
    const char* _str; 

內部字符串不應爲const,你應該可以稍後調整它

const char operator[](int position) 
{ 
    return _str[position]; 
} 

你缺少一個檢查:length() > position

void operator=(string s) 
{ 
    _str = s.c_str(); 
} 

你是不是修改s,它應該是const string& s 您還沒有複製s.c_str()這意味着現在sthis都共享同一個內部緩衝區

void append(string s) // s should be a const reference too 
{ 
    size_t nlength = (length() + s.length()); 
    char* nstr = new char[nlength]; 
    for (int i = 0; i < length(); ++i) 
    { 
     nstr[i] = _str[i]; 
    } 
    for (int i = length() + 1; i < nlength; ++i) 
    { 
     nstr[i] = s[(i - length() - 1)]; 
    } 
    _str = const_cast<const char*>(nstr); 
} 

應該更容易寫在條款的realloc

void append(string s) 
{ 
    int start = length(); 
    _str = realloc(_str, length() + s.length()); 
    for (int i = 0; i < s.length(); i++) { 
     _str[start+i] = s[i]; 
    } 
} 

如果你要堅持new其確定,但您必須先將其分配給_str,然後才能將其分配給新的。

下運營商應該是const:

const char* c_str() const; 
size_t length() const; 
std::string str(); 

更新:爲構造選項:

// option one (use delete[] to cleanup _str) 
string(const char* s) { 
    int n = strlen(s); 
    _str = new char[n+1]; 
    memcpy(_str, s, n+1); // s is NULL terminated 
} 

// option two (use free() to cleanup _str) 
string(const char* s) { 
    int n = strlen(s); 
    _str = (char*)malloc(n+1); 
    memcpy(_str, s, n+1); // s is NULL terminated 
} 

// option 3: rely on append taking a char* argument 
string(const char *s) : _str(NULL) { 
    append(s, strlen(s)); 
} 
.. 
void append(const string& s) { 
    append(s.c_str(), s.length()) 
} 
void append(const char *s, int len) { 
    int start = _str ? length() : 0; 
    _str = realloc(_str, start + len); 
    for (int i = 0; i < len; i++) { 
     _str[start+i] = s[i]; 
    } 
} 

更新2:這將是更好地使用size_tunsigned int代替平原int,因爲大小總是大於或等於零。

+3

不要從'malloc' /'realloc'的'new'或'delete'中釋放某些東西。壞事可能發生。見http://www.parashift.com/c++-faq/mixing-malloc-and-delete.html –

+0

@FredLarson是如此真實:) –

+1

我說過,因爲我非常關心你對realloc的推薦,當你在析構函數中觸發'delete []'時會很糟糕。 –