Hello. I solve 2 problem 9 chapters from the book by R. Lafore . Stumbled upon a problem. Task: Recall the STRCONV example from chapter 8. The String class in this example has a defect: it does not have protection in case its objects are initialized to be too long (the size constant has the value 15). For example: String s = "Эта строка имеет очень большую длину и мы можем быть уверены, что она не уместится в отведенный буфер, что приведет к непредсказуемым последствиям."; will cause str array to overflow with string s with unpredictable consequences up to system crash. Create a class Pstring , derived from the class String , in which we prevent the possibility of a buffer overflow in the definition of too long a string constant. The new constructor of the derived class will copy only size-1 characters to str if the line is too long and will copy the entire line if it is shorter than size . Write the main() function of the program to test its work with strings of different lengths.

 #include <iostream> #include <cstring> const int size = 15; class String { protected: char str[size]; public: String () { str[0] = '\x0'; } String (const char* s) { strcpy(str, s); std::cout << "Скопировано: " << str << std::endl; } void display () const { std::cout << str << std::endl; } operator char* () { return str; } }; class Pstring : public String { public: Pstring (char* s) { if (strlen(s) >= size) { for (int index = 0; index < size - 1; ++index) { str[index] = s[index]; } str[strlen(str)] = '\x0'; } else { String(s); // почему не копируется строка, а вызывается конструктор без аргументов? } } }; int main () { Pstring p1 = "pointers is evil!!!"; p1.display (); Pstring p2 = "char * is bad"; p2.display(); return 0; } 

When defining variables of the Pstring class, the default constructor is first called String :: String (); before block execution. Next, there is a check for the length of the string. Question: Why, if the string is smaller, then when the constructor is called String (s); Is the string value not copied to the else branch? Why the output is an empty string.

    2 answers 2

    You have several problems with defining constructors in the Pstring class. First, it is desirable that it behaves like a String class, which means that it must also have a default constructor.

     Pstring() {} 

    Secondly, you defined constructor with a parameter is completely wrong.

    In this sentence the constructor

     str[strlen(str)] = '\x0'; 

    immediately two errors are present. The string str does not yet have a terminating zero, so calling strlen( str ) leads to undefined behavior. In addition, you must set to 0 the character at position size - 1

    In this sentence

     String(s); 

    You create a temporary object, which is immediately deleted at the end of the sentence. It has nothing to do with the object in the constructor of which this sentence is invoked.

    Keep in mind that when you are dealing with strings, and even more so with standard C functions designed to work with strings, it is better to use the size_t type instead of the int type as an index in the strings.

    Also, as a constructor parameter, you should set a parameter with the type const char * , since, as at least it follows from your program, the constructor can deal with string literals as arguments, and string literals in C ++ are converted in expressions (with rare exception) to type const char * .

    Further, the length of a member of the class str is an internal property of the class itself. Therefore, its length should not be a global variable.

    And finally, it is desirable that the destructor is virtual.

    I would define these classes as follows.

     class String { protected: static const size_t size = 15; char str[size]; public: String () { str[0] = '\x0'; } String (const char* s) { std::strcpy(str, s); std::cout << "Скопировано: " << str << std::endl; } virtual ~String() {} void display () const { std::cout << str << std::endl; } operator const char* () const { return str; } }; class Pstring : public String { public: Pstring() {} Pstring ( const char* s) { if ( std::strlen(s) < size) { std::strcpy( str, s ); } else { size_t i = 0; for ( ; i < size - 1; ++i) { str[i] = s[index]; } str[i] = '\x0'; // или просто //std::strncpy( str, s, size - 1 ); // str[size-1] = '\0'; } } }; 

      You have here

       String(s); 

      just declare a variable of type String . Naturally, the default constructor is called for it.

      You cannot simply take and call a constructor in the middle of another function, like a normal function.

      Rewrite your constructor as

       Pstring(const char* s) { if (s) { strncpy(str,s,size-1); str[size-1] = 0; } } 

      With s==nullptr I do not do anything, because the default base class constructor already zeros the first character.

      By the way, I would recommend doing the appropriate verification in String .