6

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

4

4 回答 4

4

该行:

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 被构造后被销毁(调用析构函数)。

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

于 2010-05-16T10:57:45.787 回答
3

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

此外,您忘记将 operator const char* 设为 const 重载。

于 2010-05-16T11:35:24.150 回答
1

这是发生了什么:

  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 调试器下它没有打印任何东西。

于 2010-05-16T11:10:33.890 回答
0

你犯的几个错误:

1. 拷贝构造函数签名错误。一定是:

CString(const CString& q)

2. op=签名错误。一定是:

CString& operator=(const CString& q)

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

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

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

CString::CString()
{
   cstr = new char[1];
   cstr[0] = 0;
}
于 2010-05-16T12:07:59.333 回答