我正在尝试用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();
}
答案 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()
,这意味着现在s
和this
共享相同的内部缓冲区
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_t
或unsigned 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
。这样您就可以construct
,destroy
元素,allocate
和deallocate
内存。你一定会以这种方式让你的记忆更加安全。
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()
知道何时停止读取字符。