关于字符串类实现的C ++考试

时间:2010-05-07 21:45:55

标签: c++ string character

我刚参加考试时被问到以下问题:

  

为下面给出的代码编写GenStrLen,InsertChar和StrReverse的每个方法的函数体。您必须考虑以下因素;

     
      
  • 如何在C ++中构造字符串
  •   
  • 字符串不得溢出
  •   
  • 插入字符会使其长度增加1
  •   
  • 空字符串由StrLen = 0
  • 表示   
class Strings {
  private:
    char str[80];
    int StrLen;
  public:

  // Constructor
  Strings() {
    StrLen=0;
  };

  // A function for returning the length of the string 'str'
  int GetStrLen(void) {

  };

  // A function to inser a character 'ch' at the end of the string 'str'
  void InsertChar(char ch) {

  };

  // A function to reverse the content of the string 'str'
  void StrReverse(void) {

  };

};

我给出的答案是这样的(见下文)。我的一个问题是,使用了许多额外的变量,这让我相信不是以最好的方式做到这一点,而另一件事是不能正常工作....

class Strings {
private:
    char str[80];
    int StrLen;
    int index; // *** Had to add this ***
public:

    Strings(){
        StrLen=0;
    }

    int GetStrLen(void){
        for (int i=0 ; str[i]!='\0' ; i++)
            index++;
        return index;   // *** Here am getting a weird value, something like 1829584505306 ***
    }

    void InsertChar(char ch){    
        str[index] = ch;  // *** Not sure if this is correct cuz I was not given int index ***
    }

    void StrRevrse(void){
        GetStrLen();
        char revStr[index+1];
        for (int i=0 ; str[i]!='\0' ; i++){
            for (int r=index ; r>0 ; r--)
                revStr[r] = str[i];
        }
    }
};

如果有人能够大致解释一下解答问题的最佳方式以及原因,我将不胜感激。另外,为什么我的教授会关闭每个类函数,比如“};”,我认为它只用于结束类和构造函数。

非常感谢你的帮助。

11 个答案:

答案 0 :(得分:17)

首先,琐碎的};问题只是风格问题。当我将函数体放在类声明中时,我也这样做。在这种情况下,;只是一个空语句,并不会改变程序的含义。它可以被排除在函数的末尾(但是不是类的结尾)。

以下是您所写内容的一些主要问题:

  1. 您永远不会初始化str的内容。它不能保证以\0字节开头。
  2. 您永远不会初始化index,只能在GetStrLen内进行设置。程序启动时它可能具有值-19281281。如果有人在致电InsertChar之前致电GetStrLen该怎么办?
  3. 您永远不会更新index中的InsertChar。如果有人连续两次拨打InsertChar该怎么办?
  4. StrReverse中,您创建一个名为revStr的反向字符串,但之后您永远不会对其执行任何操作。 str中的字符串保持相同的后缀。
  5. 令我感到困惑的部分是为什么你创建了一个名为index的新变量,大概是为了跟踪字符串的最后一个字符的索引,当时已经存在一个名为{{1}的变量为了这个目的,你完全忽略了。最后一个字符的索引是字符串的长度,所以你应该保持字符串的长度是最新的,并使用它,例如。

    StrLen

    然而,你的字符串反转算法完全错误。仔细考虑代码所说的(假设int GetStrLen(void){ return StrLen; } void InsertChar(char ch){ if (StrLen < 80) { str[StrLen] = ch; StrLen = StrLen + 1; // Update the length of the string } else { // Do not allow the string to overflow. Normally, you would throw an exception here // but if you don't know what that is, you instructor was probably just expecting // you to return without trying to insert the character. throw std::overflow_error(); } } 在其他地方正确初始化和更新)。它说“对于index中的每个角色,用这个角色向后覆盖整个str”。如果revStr开始时为str,则"Hello World"最终会显示为revStr,因为"ddddddddddd"d中的最后一个字符。

    你应该做的是这样的事情:

    str

    注意它是如何工作的。说void StrReverse() { char revStr[80]; for (int i = 0; i < StrLen; ++i) { revStr[(StrLen - 1) - i] = str[i]; } } 。然后我们将StrLen = 10的第0位复制到str的第9位,然后将revStr的第1位复制到str的位置9等等,直到我们将revStr的{​​{1}}位置复制到StrLen - 1的位置0。

    但是你在str中有一个反向字符串,你仍然错过了将它放回revStr的部分,所以完整的方法看起来像

    revStr

    并且有更聪明的方法可以做到这一点,你不必拥有一个临时字符串str,但上述功能完全正常,并且是问题的正确答案。

    顺便说一句,你真的不需要担心这段代码中的NULL字节(void StrReverse() { char revStr[80]; for (int i = 0; i < StrLen; ++i) { revStr[(StrLen - 1) - i] = str[i]; } for (int i = 0; i < StrLen; ++i) { str[i] = revStr[i]; } } s)。您(或者至少应该)使用revStr变量跟踪字符串的长度这一事实使得最终的哨兵变得不必要,因为使用\0您已经知道StrLen以外的内容{ {1}}应该被忽略。

答案 1 :(得分:4)

int GetStrLen(void){
    for (int i=0 ; str[i]!='\0' ; i++)
        index++;
    return index;   // *** Here am getting a weird value, something like 1829584505306 ***
}

你得到一个奇怪的值,因为你从未初始化索引,你刚刚开始递增它。

答案 2 :(得分:4)

您的GetStrLen()函数无效,因为str数组未初始化。它可能不包含任何零元素。

您不需要index成员。只需使用StrLen跟踪当前字符串长度。

答案 3 :(得分:4)

通过这个考试题目可以学到很多有趣的课程。首先,审查员本身并不是一个流利的C ++程序员!您可能希望查看代码的样式,包括变量和方法名称是否有意义,以及您对(void)const等的使用给出的其他一些注释。 ...方法名称真的需要“Str”吗?毕竟,我们正在使用“Strings”课程!

对于“如何在C ++中构造字符串”,以及(如在C中)这些是以null结尾的,并且不像它们那样存储长度,就像Pascal(和这个类)那样。 [@Gustavo,strlen()在这里不起作用,因为字符串不是以空字符结尾的字符串。]在“真实世界”中,我们使用std::string类。

“字符串不能溢出”,但是类的用户如何知道他们是否尝试溢出字符串。 @ Tyler关于抛出std::overflow_exception(也许带有消息)的建议会起作用,但是如果你正在编写自己的字符串类(纯粹作为练习,你在现实生活中不太可能需要这样做)那么你应该提供自己的异常类。

“插入字符会使其长度增加1”,这意味着GetStrLen()不会计算字符串的长度,而是纯粹返回在构造时初始化并通过插入更新的StrLen的值。

您可能还想考虑如何测试您的课程。为了便于说明,我添加了一个Print()方法,以便您可以查看该类的内容,但您应该查看类似Cpp Unit Lite的内容。

对于它的价值,我包括我自己的实现。与目前为止的其他实现不同,我选择在反向函数及其交换帮助器中使用原始指针。我假设使用诸如std::swapstd::reverse之类的内容超出了本次考试的范围,但是您需要熟悉标准库,以便您可以继续编程而无需重新发明轮子

#include <iostream>

void swap_chars(char* left, char* right) {
    char temp = *left;
    *left = *right;
    *right = temp;
}

class Strings {
private:
    char m_buffer[80];
    int m_length;
public:

    // Constructor
    Strings() 
        :m_length(0) 
    {
    }

    // A function for returning the length of the string 'm_buffer'
    int GetLength() const {
        return m_length;
    }

    // A function to inser a character 'ch' at the end of the string 'm_buffer'
    void InsertChar(char ch) {
        if (m_length < sizeof m_buffer) {
            m_buffer[m_length++] = ch;
        }
    }

    // A function to reverse the content of the string 'm_buffer'
    void Reverse() {
        char* left = &m_buffer[0];
        char* right = &m_buffer[m_length - 1];
        for (; left < right; ++left, --right) {
            swap_chars(left, right);
        }
    }


    void Print() const {
        for (int index = 0; index < m_length; ++index) {
            std::cout << m_buffer[index];
        }
        std::cout << std::endl;
    }
};

int main(int, char**) {
    Strings test_string;

    char test[] = "This is a test string!This is a test string!This is a test string!This is a test string!\000";
    for (char* c = test; *c; ++c) {
        test_string.InsertChar(*c);
    }

    test_string.Print();
    test_string.Reverse();
    test_string.Print();

    // The output of this program should look like this...
    // This is a test string!This is a test string!This is a test string!This is a test
    // tset a si sihT!gnirts tset a si sihT!gnirts tset a si sihT!gnirts tset a si sihT

    return 0;
}

祝你好好继续学习!

答案 4 :(得分:2)

初始化char数组时,应将其第一个元素设置为0,将index设置为相同。因此,你在GetStrLen得到一个奇怪的长度,因为当你找到你正在寻找的0时它取决于神。

[更新] 在C / C ++中,如果没有显式初始化变量,通常会让它们充满随机垃圾(分配给它们的原始内存的内容)。此规则有一些例外,但最佳做法是始终显式初始化变量。 [/ Update]

InsertChar中,你应该(在检查溢出之后)使用StrLen来索引数组(因为注释指定“在字符串'str'的末尾加上一个字符'ch'” ),然后设置新的终止0字符并增加StrLen

答案 5 :(得分:2)

void InsertChar(char ch){    
   str[index] = ch;  // *** Not sure if this is correct cuz I was not given int index ***
}

这应该更像是

str[strlen-1]=ch; //overwrite the null with ch
str[strlen]='\0'; //re-add the null
strlen++;

答案 6 :(得分:2)

你的老师在这个问题上给了你非常好的提示,再读一遍并尝试回答你自己。这是我未经测试的解决方案:

class Strings {
  private:
    char str[80];
    int StrLen;
  public:

  // Constructor
  Strings() {
    StrLen=0;
    str[0]=0;
  };

  // A function for returning the length of the string 'str'
  int GetStrLen(void) {
    return StrLen;
  };

  // A function to inser a character 'ch' at the end of the string 'str'
  void InsertChar(char ch) {
    if(StrLen < 80)
      str[StrLen++]=ch;
  };

  // A function to reverse the content of the string 'str'
  void StrReverse(void) {
    for(int i=0; i<StrLen / 2; ++i) {
      char aux = str[i];
      str[i] = str[StrLen - i - 1];
      str[StrLen - i - 1] = aux;
    }
  };
};

答案 7 :(得分:2)

您不需要index作为会员数据。你可以在GetStrLen()中将它变成一个局部变量:只需在那里声明它而不是在类体中声明它。返回index时获得奇怪值的原因是因为您从未初始化它。要解决此问题,请在index中将GetStrLen()初始化为零。

但是有一种更好的方法:当你通过InsertChar()插入一个字符时,增加StrLen的值,这样GetStrLen()只需要返回该值。这将使GetStrLen()更快:它将以恒定时间运行(无论字符串的长度如何,都是相同的性能)。

InsertChar()中,您可以使用StrLen作为索引,而不是index,我们已经确定这是多余的。但请记住,您必须确保字符串以'\0'值终止。还要记住通过增加它来维持StrLen以使GetStrLen()的生活更轻松。此外,您必须在InsertChar()中执行额外步骤以避免缓冲区溢出。当字符串的长度为79个字符时,用户在字符串中插入字符时会发生这种情况。 (是的,79:你必须在终止null上花费一个字符。)

在发生这种情况时,我没有看到关于如何表现的指示,所以它必须取决于你良好的判断力。如果用户尝试添加第80个字符,您可能会忽略该请求并返回,或者您可能会设置错误标记 - 这取决于您。

StrReverse()函数中,您有一些错误。首先,您调用GetStrLen()但忽略其返回值。那为什么叫呢?其次,您正在创建一个临时字符串并对其进行处理,而不是在该类的字符串成员上。所以你的函数不会改变字符串成员,实际上它应该反转它。最后,你可以通过迭代它的一半来更快地反转字符串。

处理成员数据字符串。要反转字符串,您可以将字符串的第一个元素(字符)与其最后一个(不是终止空值,前面的字符!)交换,第二个元素使用倒数第二个,依此类推。当你到达弦乐的中间时,你已经完成了。不要忘记字符串必须以'\0'字符终止。

当你在解决考试时,这也是一个很好的机会,可以教导你的教师一两个关于C ++的思考:我们不会说f(void),因为那属于C89的旧时代。在C ++中我们说f()。我们还努力在C ++中尽可能使用类初始化列表。还要提醒你的教师const正确性有多重要:当一个函数不应该改变时,对象应该被标记为这样。 int GetStrLen(void)应为int GetStrLen() const

答案 8 :(得分:1)

你不需要弄清楚长度。你已经知道它是strLen。此外,原始问题中没有任何内容表明缓冲区应包含空终止字符串。

int GetStrLen(void){
    return strLen;
}

只是在这里使用断言,但另一种选择是抛出异常。

void InsertChar(char ch){
    assert(strLen < 80);
    str[strLen++] = ch;
}

反转字符串只是交换str缓冲区中的元素。

void StrRevrse(void){
    int n = strLen >> 1;
    for (int i = 0; i < n; i++) {
        char c = str[i];
        str[i] = str[strLen - i];
        str[strLen - i] = c;
    }
}

答案 9 :(得分:1)

我会使用StrLen来跟踪字符串的长度。由于长度也表示字符串的结尾,我们可以使用它来插入:

int GetStrLen(void) {
    return StrLen;
}

int InsertChar(char ch)
{
    if (strLen < sizeof(str))
    {
        str[StrLen] = ch;
        ++strLen;
    }
}

void StrReverse(void) {
    for (int n = 0; n < StrLen / 2; ++n)
    {
        char tmp = str[n];
        str[n] = str[StrLen - n - 1];
        str[StrLen - n - 1] = tmp;
    }
}

答案 10 :(得分:1)

首先为什么使用String.h作为字符串长度? strlen(char [] array)将Lenght或任何char数组返回给int。

你的函数返回一个werid值,因为你从不初始化索引,并且数组的值为零,首先启动然后执行你的方法。