#cpp #классы #конструктор #объявление
Здравствуйте. Решаю 2 задачу 9 главы из книги Р. Лафоре. Наткнулся на проблему. Задача: Вспомните пример STRCONV из главы 8. Класс String в этом примере имеет дефект: у него нет защиты на тот случай, если его объекты будут инициализированы слишком длинной строкой (константа size имеет значение 15). Например: String s = "Эта строка имеет очень большую длину и мы можем быть уверены, что она не уместится в отведенный буфер, что приведет к непредсказуемым последствиям."; будет причиной переполнения массива str строкой s с непредсказуемыми последствиями вплоть до краха системы. Создадим класс Pstring, производный от класса String, в котором предотвратим возможность переполнения буфера при определении слишком длинной строковой константы. Новый конструктор производного класса будет копировать в str только size-1 символов, если строка окажется слишком длинной, и будет копировать строку полностью, если она будет иметь длину меньшую, чем size. Напишите функцию main() программы для проверки ее работы со строками разной длины. #include#include 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; } При определении переменных класса Pstring вначале вызывается конструктор по умолчанию String::String(); до выполнения блока. Далее, идёт проверка на длину строки. Вопрос: Почему, если строка меньшего размера, то при вызове конструктора String(s); в ветви else не копируется значение строки? Почему на выводе пустая строка.
Ответы
Ответ 1
У вас имеется несколько проблем с определением конструкторов в классе Pstring. Во-первых, желательно, чтобы он вел себя как класс String, что означает, что он также должен иметь конструктор по умолчанию. Pstring() {} Во-вторых, вами определенный конструктор с параметром полностью неверный. В этом предложении конструктора str[strlen(str)] = '\x0'; присутствуют сразу же две ошибки. Строка str не имеет пока еще завершающего нуля, поэтому вызов strlen( str ) ведет к неопределенному поведению. К тому же вы должны установить в 0 символ в позиции size - 1 В этом предложении String(s); У вас создается временный объект, который сразу же удаляется в конце предложения. Он никак не связан с тем объектом, в конструкторе которого вызывается это предложение. Имейте в виду, что когда вы имеете дело со строками, а тем более со стандартными C функциями, предназначенными для работы со строками, то лучше в качестве индекса в строках использовать тип size_t вместо типа int. Также в качестве параметра конструктора следует задать параметр с типом const char *, так как, как по крайней мере следует из вашей программы, конструктор может иметь дело со строковыми литералами в качестве аргументов, а строковые литералы в C++ в выражениях преобразуются (за редким исключением) к типу const char *. Далее длина члена класса str - это внутреннее свойство самого класса. Поэтому его длина не должна быть глобальной переменной. И, наконец, желательно, чтобы деструктор был виртуальным. Я бы определили данные классы следующим образом 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'; } } };Ответ 2
У вас здесь String(s); просто объявление переменной s типа String. Естественно, для нее вызывается конструктор по умолчанию. Вы не можете просто взять и вызвать конструктор посреди другой функции, как обычную функцию. Перепишите свой конструктор как Pstring(const char* s) { if (s) { strncpy(str,s,size-1); str[size-1] = 0; } } При s==nullptr я ничего не делаю, потому что конструктор базового класса по умолчанию уже зануляет первый символ. Кстати, рекомендовал бы и в String делать соответствующую проверку.
Комментариев нет:
Отправить комментарий