как исправить «Местоположение чтения с нарушением доступа» в этом коде

#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 классе.