#c #destructor
#c #деструктор
Вопрос:
запустив этот код, я получил некоторую ошибку, подобную этой
«Исключение, вызванное 0x778D7FCB (ntdll.dll) в Project1.exe: 0xC0000005: местоположение чтения с нарушением доступа 0x00000014.’
Эта ошибка возникает в этой строке
~UnivStudnet() {
delete[]major; // error
}
#include <iostream>
#include <cstring>
using namespace std;
class Person {
private:
char * name;
public:
Person(const char * myname) {
name = new char[strlen(myname) 1];
strcpy_s(name, strlen(name), myname);
}
~Person() {
delete[]name;
}
void WhatYourName() const {
cout << "My name is " << name << endl;
}
};
class UnivStudnet : public Person {
private:
char * major;
public:
UnivStudnet(const char * myname, const char * const mymajor) :Person(myname) {
major = new char[strlen(mymajor) 1];
strcpy_s(major, strlen(major), mymajor);
}
~UnivStudnet() {
delete[]major;
}
void WhoAreYou() const {
WhatYourName();
cout << "My major is " << major << endl;
}
};
int main(void) {
UnivStudnet st1("kim", "Mathenatics");
st1.WhoAreYou();
UnivStudnet st2("hong", "Physiscs");
st2.WhoAreYou();
return 0;
}
Как мне исправить эту ошибку?
Комментарии:
1. @hsalimi даже здесь лучше сделать вообще, что не имеет значения, потому что нет удаления из указателя базового класса
2. Также реализуйте конструктор копирования / перемещения / присваивания, в противном случае вы дважды удалите необработанный указатель, если будете выполнять операции копирования.
3. Вы уверены, что код, который вы нам предоставляете, приводит к недопустимому доступу? Почему вы не используете std::string ?
Ответ №1:
В двух strcpy_s
строках есть ошибки.
strcpy_s(name, strlen(name), myname);
должно быть
strcpy_s(name, strlen(myname) 1, myname);
и аналогично
strcpy_s(major, strlen(major), mymajor);
должно быть
strcpy_s(major, strlen(mymajor) 1, mymajor);
Вызов strlen
вновь выделенных массивов символов name
и major
, которые имеют неопределенные значения, вызывает неопределенное поведение, которое является причиной вашего сбоя.
Комментарии:
1. Я думаю, что операция прошла по документации , в которой указано, что второй параметр — это размер строки назначения.
2. Что здесь есть
strlen(myname) 1
, поскольку этот размер изменен в строке чуть выше.strlen(name)
не выдает выделенную длинуname
.3. Да, вы правы. Я просто хотел указать, почему OP мог допустить эту ошибку (кроме того, что не добавил 1). Кстати, поддержал ответ. 🙂
Ответ №2:
Ваше использование strcpy_s вызывает подозрение.
major = new char[strlen(mymajor) 1];
strcpy_s(major, strlen(major), mymajor);
Вторым параметром для strcpy_s
является выделенный размер буфера, указанный первым параметром. (И я только сейчас понял — на основе другого ответа, который strlen(major)
не определен перед копированием в него!
Вы выделяете буфер, который должен быть достаточно большим для хранения строки, но последующий вызов strcpy_s указывает, что major
недостаточно большой, чтобы вместить всю строку, включая символ null.
Лучше:
size_t len = strlen(mymajor) 1;
major = new char[len];
strcpy_s(major, len, mymajor);
Повторите приведенный выше шаблон также для name
параметра базового класса.
Ответ №3:
Вы могли бы пойти другим путем в C :
Вам нужно объявить:
virtual ~Person()
деструктор в базовом классе, а затем:
class UnivStudnet : public Person {
private:
std::string major;
public:
UnivStudnet(const char * myname, const char * const mymajor) :Person(myname), major(mymajor) {
}
virtual ~UnivStudnet() {
}
...
Таким образом, вы достигнете того, что вам нужно, и не будете думать о выделении / освобождении памяти. Не забудьте #include <string>
заголовок.
Таким же образом сделайте это в Person
классе.