Страницы

Поиск по вопросам

вторник, 28 января 2020 г.

Вызов конструктора без аргументов?

#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 делать соответствующую проверку.

Комментариев нет:

Отправить комментарий