开发者

C++ exam on string class implementation

开发者 https://www.devze.com 2022-12-29 07:35 出处:网络
I just took an exam where I was asked the following: Write the function body of each of the methods GenStrLen, InsertChar and StrReverse for the given code below. You must take into consideration th

I just took an exam where I was asked the following:

Write the function body of each of the methods GenStrLen, InsertChar and StrReverse for the given code below. You must take into consideration the following;

  • How strings are constructed in C++
  • The string must not overflow
  • Insertion of character increases its length by 1
  • An empty string is indicated by 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) {

  };

};

The answer I gave was something like this (see bellow). My one of problem is that used many extra variables and that makes me believe am not doing it the best possible way, and the other thing is that is not working....

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++;
    开发者_JAVA技巧    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];
        }
    }
};

I would appreciate if anyone could explain me roughly what is the best way to have answered the question and why. Also how come my professor closes each class function like " }; ", I thought that was only used for ending classes and constructors only.

Thanks a lot for your help.


First, the trivial }; question is just a matter of style. I do that too when I put function bodies inside class declarations. In that case the ; is just an empty statement and doesn't change the meaning of the program. It can be left out of the end of the functions (but not the end of the class).

Here's some major problems with what you wrote:

  1. You never initialize the contents of str. It's not guaranteed to start out with \0 bytes.
  2. You never initialize index, you only set it within GetStrLen. It could have value -19281281 when the program starts. What if someone calls InsertChar before they call GetStrLen?
  3. You never update index in InsertChar. What if someone calls InsertChar twice in a row?
  4. In StrReverse, you create a reversed string called revStr, but then you never do anything with it. The string in str stays the same afterwords.

The confusing part to me is why you created a new variable called index, presumably to track the index of one-past-the-last character the string, when there was already a variable called StrLen for this purpose, which you totally ignored. The index of of one-past-the-last character is the length of the string, so you should just have kept the length of the string up to date, and used that, e.g.

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

Your algorithm for string reversal, however, is just completely wrong. Think through what that code says (assuming index is initialized and updated correctly elsewhere). It says "for every character in str, overwrite the entirety of revStr, backwards, with this character". If str started out as "Hello World", revStr would end up as "ddddddddddd", since d is the last character in str.

What you should do is something like this:

void StrReverse() {
   char revStr[80];
   for (int i = 0; i < StrLen; ++i) {
     revStr[(StrLen - 1) - i] = str[i];
   }
}

Take note of how that works. Say that StrLen = 10. Then we're copying position 0 of str into position 9 of revStr, and then position 1 of str into position 9 of revStr, etc, etc, until we copy position StrLen - 1 of str into position 0 of revStr.

But then you've got a reversed string in revStr and you're still missing the part where you put that back into str, so the complete method would look like

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];
   }
}

And there are cleverer ways to do this where you don't have to have a temporary string revStr, but the above is perfectly functional and would be a correct answer to the problem.

By the way, you really don't need to worry about NULL bytes (\0s) at all in this code. The fact that you are (or at least you should be) tracking the length of the string with the StrLen variable makes the end sentinel unnecessary since using StrLen you already know the point beyond which the contents of str should be ignored.


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

You are getting a weird value because you never initialized index, you just started incrementing it.


Your GetStrLen() function doesn't work because the str array is uninitialized. It probably doesn't contain any zero elements.

You don't need the index member. Just use StrLen to keep track of the current string length.


There are lots of interesting lessons to learn by this exam question. Firstly the examiner is does not appear to a fluent C++ programmer themselves! You might want to look at the style of the code, including whether the variables and method names are meaningful as well as some of the other comments you've been given about usage of (void), const, etc... Do the method names really need "Str" in them? We are operating with a "Strings" class, after all!

For "How strings are constructed in C++", well (like in C) these are null-terminated and don't store the length with them, like Pascal (and this class) does. [@Gustavo, strlen() will not work here, since the string is not a null-terminated one.] In the "real world" we'd use the std::string class.

"The string must not overflow", but how does the user of the class know if they try to overflow the string. @Tyler's suggestion of throwing a std::overflow_exception (perhaps with a message) would work, but if you are writing your own string class (purely as an exercise, you're very unlikely to need to do so in real life) then you should probably provide your own exception class.

"Insertion of character increases its length by 1", this implies that GetStrLen() doesn't calculate the length of the string, but purely returns the value of StrLen initialised at construction and updated with insertion.

You might also want to think about how you're going to test your class. For illustrative purposes, I added a Print() method so that you can look at the contents of the class, but you should probably take a look at something like Cpp Unit Lite.

For what it's worth, I'm including my own implementation. Unlike the other implementations so far, I have chosen to use raw-pointers in the reverse function and its swap helper. I have presumed that using things like std::swap and std::reverse are outside the scope of this examination, but you will want to familiarise yourself with the Standard Library so that you can get on and program without re-inventing wheels.

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

Good luck with the rest of your studies!


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

This should be something more like

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


Your teacher gave you very good hints on the question, read it again and try answering yourself. Here's my untested solution:

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;
    }
  };
};


When you init the char array, you should set its first element to 0, and the same for index. Thus you get a weird length in GetStrLen since it is up to the gods when you find the 0 you are looking for.

[Update] In C/C++ if you do not explicitly initialize your variables, you usually get them filled with random garbage (the content of the raw memory allocated to them). There are some exceptions to this rule, but the best practice is to always initialize your variables explicitly. [/Update]

In InsertChar, you should (after checking for overflow) use StrLen to index the array (as the comment specifies "inser a character 'ch' at the end of the string 'str'"), then set the new terminating 0 character and increment StrLen.


You don't need index as a member data. You can have it a local variable if you so please in GetStrLen(): just declare it there rather than in the class body. The reason you get a weird value when you return index is because you never initialized it. To fix that, initialize index to zero in GetStrLen().

But there's a better way to do things: when you insert a character via InsertChar() increment the value of StrLen, so that GetStrLen() need only return that value. This will make GetStrLen() much faster: it will run in constant time (the same performance regardless of the length of string).

In InsertChar() you can use StrLen as you index rather than index, which we already determined is redundant. But remember that you must make sure the string terminates with a '\0' value. Also remember to maintain StrLen by incrementing it to make GetStrLen()'s life easier. In addition, you must take the extra step in InsertChar() to avoid a buffer overflow. This happens when the user inserts a character to the string when the length of the string is alreay 79 characters. (Yes, 79: you must spend one character on the terminating null).

I don't see an instruction as to how to behave when that happens, so it must be up to your good judgment call. If the user tries to add the 80th character you might ignore the request and return, or you might set an error flag -- it's up to you.

In your StrReverse() function you have a few mistakes. First, you call GetStrLen() but ignore its return value. Then why call it? Second, you're creating a temporary string and work on that, rather than on the string member of the class. So your function doesn't change the string member, when it should in fact reverse it. And last, you could reverse the string faster by iterating through half of it only.

Work on the member data string. To reverse a string you can swap the first element (character) of the string with its last (not the terminating null, the character just before that!), the second element with the second-to-last and so on. You're done when you arrive at the middle of the string. Don't forget that the string must terminate with a '\0' character.

While you were solving the exam it would also be a good opportunity to teach your instructor a think or two about C++: we don't say f(void) because that belongs to the old days of C89. In C++ we say f(). We also strive in C++ to use class initializer lists whenever we can. Also remind your instructor how important const-correctness is: when a function shouldn't change the object is should be marked as such. int GetStrLen(void) should be int GetStrLen() const.


You don't need to figure out the length. You already know it it is strLen. Also there was nothing in the original question to indicate that the buffer should contain a null terminated string.

int GetStrLen(void){
    return strLen;
}

Just using an assertion here but another option is to throw an exception.

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

Reversing the string is just a matter of swapping the elements in the str buffer.

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;
    }
}


I would use StrLen to track the length of the string. Since the length also indicates the end of the string, we can use that for inserting:

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;
    }
}


first of all why on you use String.h for the string length? strlen(char[] array) returns the Lenght or any char array to a int.

Your function return a werid value because you never initialize index, and the array has zero values, first initilize then execute your method.

0

精彩评论

暂无评论...
验证码 换一张
取 消