Здравствуйте. Решаю 2 задачу 9 главы из книги Р. Лафоре. Наткнулся на проблему.
Задача:
Вспомните пример STRCONV из главы 8. Класс String в этом примере имеет дефект: у него нет защиты на тот случай, если его объекты будут инициализированы слишком длинной строкой (константа size имеет значение 15). Например: String s = "Эта строка имеет очень большую длину и мы можем быть уверены, что она не уместится в отведенный буфер, что приведет к непредсказуемым последствиям."; будет причиной переполнения массива str строкой s с непредсказуемыми последствиями вплоть до краха системы. Создадим класс Pstring, производный от класса String, в котором предотвратим возможность переполнения буфера при определении слишком длинной строковой константы. Новый конструктор производного класса будет копировать в str только size-1 символов, если строка окажется слишком длинной, и будет копировать строку полностью, если она будет иметь длину меньшую, чем size. Напишите функцию main() программы для проверки ее работы со строками разной длины.
#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 не копируется значение строки? Почему на выводе пустая строка.
Ответ
У вас имеется несколько проблем с определением конструкторов в классе 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';
}
}
};
Комментариев нет:
Отправить комментарий