【问题标题】:Custom string class (C++)自定义字符串类 (C++)
【发布时间】:2011-02-20 01:37:35
【问题描述】:

我正在尝试编写自己的 C++ String 类以用于教育和需求目的。
首先是我对操作符了解不多,这就是我想学习它们的原因。 我开始编写我的课程,但是当我运行它时,它会阻止程序但不会导致任何崩溃。
在进一步阅读之前,请查看以下代码:

class CString
{
private:
  char* cstr;
public:
  CString();
  CString(char* str);
  CString(CString& str);
  ~CString();

  operator char*();
  operator const char*();
  CString operator+(const CString& q)const;
     CString operator=(const CString& q);
};

首先,我不太确定我声明的一切都是正确的。我试着用谷歌搜索它,但所有关于重载的教程都解释了基本的想法,这很简单,但缺乏解释每件事是如何以及何时被调用的。例如,在我的 = 运算符中,程序调用 CString(CString& str);但我不知道为什么。
我还附上了下面的cpp文件:

CString::CString()
{
 cstr=0;
}
CString::CString(char *str)
{
 cstr=new char[strlen(str)];
 strcpy(cstr,str);
}
CString::CString(CString& q)
{
 if(this==&q)
  return;
 cstr = new char[strlen(q.cstr)+1];
 strcpy(cstr,q.cstr);
}
CString::~CString()
{
 if(cstr)
  delete[] cstr;
}
CString::operator char*()
{
 return cstr;
}
CString::operator const char* ()
{
 return cstr;
}
CString CString::operator +(const CString &q) const
{
 CString s;
 s.cstr = new char[strlen(cstr)+strlen(q.cstr)+1];
 strcpy(s.cstr,cstr);
 strcat(s.cstr,q.cstr);
 return s;
}
CString CString::operator =(const CString &q)
{
 if(this!=&q)
 {
  if(cstr)
   delete[] cstr;
  cstr = new char[strlen(q.cstr)+1];
  strcpy(cstr,q.cstr);
 }
 return *this;
}

为了测试,我使用了这么简单的代码
CString a = CString("Hello") + CString("World");
printf(a);
我尝试调试它,但有时我迷路了。首先,它为“hello”和“world”调用构造函数 2 次。然后它在 + 运算符中,这很好。然后它调用空字符串的构造函数。之后它进入“CString(CString&str)”,现在我迷路了。为什么会这样?在此之后,我注意到包含“Hello World”的字符串在析构函数中(连续几次)。我又很困惑。再次从 char* 转换为 Cstring 并来回转换后,它停止了。它永远不会进入 = 运算符,但也不会更进一步。永远无法达到 printf(a)。
我为此使用 VisualStudio 2010,但它基本上只是标准的 c++ 代码,因此我认为它不会有太大的不同

【问题讨论】:

  • 我理解教育,但“需要”的目的呢?你不喜欢标准的 std::string?
  • 坦率地说,没有。我梦想着智能且易于使用的代码。查看 CPlusplus.com 我注意到 std::string 不能做这样的事情: string a = string("Hello") + string(" world") 或 string a = String("Hello")+ " World" 因为它缺少 + 重载。更重要的是,在我的项目的这一部分中,我必须大量使用字符串,因此我更喜欢拥有一个具有我想要的所有功能以及我想要的方式的字符串。我希望我不会因为这样说而受到冷落。
  • 真正不能做的是:str3 = "qwe" + "rty";在标准中,operator+ 是字符串的全局函数。对我来说,重新定义 std::string 只是因为你想要它而不是有特定需求是初学者的错误。
  • (你甚至还没有尝试过,看看你想要的东西实际上可以通过标准实现)

标签: c++ string reference overloading operator-keyword


【解决方案1】:

你犯的几个错误:

1。复制构造函数签名错误。必须是:

CString(const CString& q)

2。 op= 签名错误。必须是:

CString& operator=(const CString& q)

顺便说一句,这也是调用复制构造函数的原因。你最后做了一个return *this,它复制了对象(使用你的 op= 签名)。

3。您允许使用 cstr == NULL 的 CString 实例(您的默认构造函数将产生这样的实例)。不过,在几乎所有函数(复制构造函数、operator +operator =)中,您都不能很好地处理这种情况(q.cstr == NULL)。

也许最简单和最安全的方法就是禁止这种情况并将默认构造函数更改为:

CString::CString()
{
   cstr = new char[1];
   cstr[0] = 0;
}

【讨论】:

  • 感谢您的建议。我更改了签名。问题是在不同的地方我看到不同的签名所以这就是为什么我不确定哪个是好的或“标准”的。而且我看到了将默认构造函数设置为 null only 终止字符串的意义。
【解决方案2】:

不要使用strlen,存储你自己的字符串长度。不应依赖字符串具有空终止符。如果传入随机的 const char* 就可以使用这样的,但是对于内部操作,你应该使用大小。

另外,您忘记将操作符 const char* 设置为 const 重载。

【讨论】:

  • 非常感谢。你告诉我的关于修复它的最后一点。
【解决方案3】:

行:

cstr=new char[strlen(str)];

应该是:

cstr=new char[strlen(str) + 1];

此外,自赋值测试在复制构造函数中没有意义 - 您正在创建一个新对象 - 它不可能与任何现有对象具有相同的地址。并且复制构造函数应该接受一个 const 引用作为参数,

如果在您的代码中,您期望使用赋值运算符,那么您的期望是错误的。这段代码:

CString a = CString("Hello") + CString(" World");

本质上等同于:

CString a( CString("Hello") + CString(" World") );

这是复制构造,而不是分配。临时 CString "Hello world" 将在 a 构造完成后被销毁(调用析构函数)。

基本上,听起来您的代码或多或少按预期工作。

【讨论】:

  • +1 GotW #11 gotw.ca/gotw/011.htm 中提到了自我分配的检查如果我没记错的话,这本书的最新版本显示了如何实际上使测试评估为真(我不知道'不记得确切的方式,但我猜是某种新的展示位置):)
  • 是的 - 它的目的是暗示这样做是可笑的。没有理由在复制构造函数中执行这样的检查,这是不可能的。
  • 是的,你是对的。自赋值测试在构造函数中没有意义,因为它在创建之前不存在。感谢您指出 +1 的事情。
【解决方案4】:

这是怎么回事:

  1. 构造函数确实被调用了两次。一次是“你好”,一次是“世界”。订单未定义。
  2. CString::operator + 在第一个 CString ("hello") 上调用,传递第二个 CString ("world") 作为它的参数。 CString::operator+的返回值是一个新的CString
  3. 由于您是在初始化时进行分配,即:CString a = [CString result of operator +],c++ 只会称您为复制构造函数。因此,您在调试器中看到了对 CString(CString& ) 的调用。

现在,总共创建了 4 个对象,一个用于每个字符串文字(“hello”和“world”),一个用于连接这些对象(CString::operator + 调用的结果,一个用于保存结果(CString a = ...)。每个临时对象都会调用它的析构函数。

至于你为什么没有得到 printf,我不知道。我只是将您的代码复制粘贴到此文件中:

#include <cstdio>
#include <cstring>

[your code]

int main(int argc,char* argv[]) {
  CString a = CString("hello") + CString(" world");
  printf(a);
}

当我运行生成的可执行文件时,我得到了hello world 作为输出。这是在带有 g++ 4.4 的 Ubuntu 上。不完全确定为什么在 VS 调试器下它没有打印任何东西。

【讨论】:

  • 我坚持了同样的事情,但显然我不得不让 operator const char* 成为一个 const 重载
猜你喜欢
  • 2011-02-06
  • 2019-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多