C ++字符串实现错误

时间:2014-01-28 18:02:26

标签: c++

我正在尝试用C ++实现一个基本的string类。但是,我被困在append()函数上。它希望它是Hello World,但结果为Hello ÍWorlýýýý««««««««þ

#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();
}

5 个答案:

答案 0 :(得分: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,然后再将其分配给新的。{/ p>

以下运算符应为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,因为大小始终大于或等于零。

答案 1 :(得分:1)

第二个循环中有一个off-by-one错误;第二个字符串需要在第一个字符串结束后立即复制到length()

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

您还需要再分配一个字节以在末尾添加空终止符。

请注意,您不需要那种可怕的强制转换来向指针添加const,因为这是一件非常安全的事情。 删除限定符只需要const_cast。您可能还想修复内存泄漏,并且对于奖励积分,请缓存长度,这样您就不必每次都读取整个字符串。

答案 2 :(得分:1)

首先,您最关键的问题是char* nstr = new char[nlength];

您必须将其更改为char* nstr = new char[nlength+1];

然后,在函数append中,在两个for循环之后,设置nstr[nlength] = 0;

其次,为了获得更好的性能(以及正确的编码),您可能需要在以下函数中将string s更改为const string& s

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

答案 3 :(得分:1)

您的代码存在<很多问题,所以让我们一步一步地执行此操作。

首先,您不需要预处理器魔术用于命名空间,而只需要基本的namespace mynamespace{}

其次,理想的是创建一个basic_string类,以便它可以与不同的字符类型一起使用。 char / wchar_t,e.t.c。

您班级的问题:

1)private: const char *_str;。指针将被修改,因此const无用。

2)没有typedef。如果您尝试重新实现STL类,则需要它们。 (在例子中解释)

3)使用allocator。这样您就可以constructdestroy元素,allocatedeallocate内存。你一定会以这种方式让你的记忆更加安全。

4)字符串必须以null结尾。这意味着最后需要额外的'\0',这意味着你必须为此分配一个额外的字节。字符串以空值终止,因为它是告诉代码停止读取字符串的一种方式。

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;

当然,仍有一点缺失,但我相信你可以通过一些在线帮助实现这一点。

答案 4 :(得分:0)

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

首先将字符复制到长度() - 1然后以长度()+ 1开始,所以你跳过一个字符,分配后可能有任何东西(除非你事先做了一个memset)。

然后你需要使用空字符(\ 0)终止你的字符串,以便在std::string方法返回时构造的str()知道何时停止读取字符。