Не получается обработать exception нарушение доступа для записи

Рейтинг: 0Ответов: 1Опубликовано: 16.05.2023

Пишу двусвязный блочный список с индексной таблицей, и он в целом работает, но если попытаться удалить или вставить элементы в конец или начало, часто вылетают исключения (нарушение доступа для записи)

И всё бы ничего, но у меня не получается их обработать, сначала пытался через обычный try catch, потом через __try __except, всё равно вылетает исключение. (В классе block с 64 по 75 строку).

Вот собственно и вопрос: как обработать такое исключение?

P.s. Не так долго изучаю программирование в целом, так что если есть советы как лучше или чище писать код буду рад узнать.

#include<iostream>
#include<cmath>
#include<string>
#include <excpt.h>

using namespace std;

string fisrtNames[8] = { "Nolah","Liam","Mason","Jacob","William","Ethan","Michael","Alexander" };
string lastNames[11] = { "Williams","Peters","Gibson","Martin","Jordan","Jackson","Grant","Davis","Collins","Bradlay","Barlow" };

class node {
public:
    node() {
        size = sizeof(values) / sizeof(*values);
        fisrtName = "";
        lastName = "";
    }
    void print() {
        cout << "values ---> \t";
        for (int i = 0; i < size; i++) {
            cout << values[i] << " ";
            if (i == size - 1)cout << "\nfirstName -> \t";
        }
        for (int i = 0; i < fisrtName.length(); i++) {
            cout << fisrtName[i];
            if (i == fisrtName.length() - 1)cout << "\nlasttName -> \t";
        }
        for (int i = 0; i < lastName.length(); i++) {
            cout << lastName[i];
            if (i == lastName.length() - 1)cout << "\n\n";
        }
    }

    int getValuesSize() { return size; }

    float* getValuesPtr() { return values; }
    void setValueByIndex(int index, float value) { values[index] = value; }

    void setFisrtName(string name) { this->fisrtName = name; }
    string getFisrtName() { return fisrtName; }

    void setLastName(string lastName) { this->lastName = lastName; }
    string getLastName() { return lastName; }

private:
    float values[7] = { 0 };
    string fisrtName;
    string lastName;
    int size;
};

class block {
public:
    block() {
        next = nullptr;
        previous = nullptr;
        countInBlock = 0;
        blocksCount++;
        number = blocksCount;
    }

    int getNumber() { return number; }

    void setNext(block* next) { this->next = next; }
    block* getNext() { return next; }

    void setPrevious(block* previous) { 
        __try {
            this->previous = previous; //не обрабатывается исключение
        }
        __except (EXCEPTION_EXECUTE_HANDLER) {
            cout << "Ошибка записи память доступна только для чтения " << endl;
        }
    }
    block* getPrevious() { return previous; }

    int getCountInBlock() { return countInBlock; }
    int getBlockSize() { return sizeof(mBlock) / sizeof(*mBlock); }

    void addInBlock(node& newNode) {
        mBlock[countInBlock] = newNode;
        countInBlock++;
    }

    void printBlock() {
        cout << "Блок номер " << number << endl;
        for (int i = 0; i < countInBlock; i++) { mBlock[i].print(); }
    }

private:
    node mBlock[2];
    block* next, * previous;
    int number, countInBlock;
    static int blocksCount;
};
int block::blocksCount = 0;

class list {
public:
    // Индексная таблица
    class indexTable {
    public:
        void createIndexTable(list& mlist) {
            listSize = mlist.getCountBlocksInList();
            offset = listSize / 4;
            if (offset == 0) offset++;

            block* nextPtr = mlist.getBegin();
            while (true) {

                if (nowPtrPos == 1) {
                    table[indexInTable] = nextPtr;
                    indexInTable++;
                }

                nextPtr = nextPtr->getNext();
                nowPtrPos++;
                if (nowPtrPos % offset == 0) {
                    table[indexInTable] = nextPtr;
                    indexInTable++;
                }
                if (nextPtr->getNext() == nullptr || indexInTable == 5) break;
            }
        }

        void printTable() {
            cout << "/------------PRINT INDEX TABLE-----------\\  \n\n";
            for (int i = 0; i < indexInTable; i++) {
                cout << table[i]->getNumber() << endl;
                table[i]->printBlock();
                cout << "|------------------NEXT------------------|\n\n";
            }
            cout << "\\---------------TABLE END-----------------/ \n\n";
        }

        int getOffset() { return offset; }

        block* getBlockByIndex(int index) { return table[index]; }
    private:
        block* table[5];
        int listSize = 0, nowPtrPos = 1, offset = 0, indexInTable = 0;
    };

    // НАЧАЛО КОНЕЦ
    enum addType {
        BEGIN,
        END
    };
    // Получение кол-ва блоков в листе
    int getCountBlocksInList() { return countBlocks; }
    //Получение указателя на первй блок
    block* getBegin() { return begin; }
    // Добавление в начало списка
    void addToStart(block* newBeginBlock) {
        begin->setPrevious(newBeginBlock);
        newBeginBlock->setNext(begin);
        this->begin = newBeginBlock;
        countBlocks++;
    }
    // Добавление в конец списка
    void addToEnd(block* newBlock) {
        if (countBlocks == 0) addToStart(newBlock);
        else {
            block* nextPtr = begin;
            while (nextPtr->getNext()) {
                nextPtr = nextPtr->getNext();
            }
            nextPtr->setNext(newBlock);
            newBlock->setPrevious(nextPtr);
            countBlocks++;
        }
    }
    // Вставка блока
    void insert(int index, block* newBlock,indexTable& table) {
        block* nextPtr;
        int offset = table.getOffset();
        if (index < offset) index--;
        nextPtr = table.getBlockByIndex((index/offset));
        for (int i = 0; i < index % offset; i++) {
            nextPtr = nextPtr->getNext();
        }
        //nextPtr->printBlock();
        //nextPtr->getPrevious()->printBlock();
        //nextPtr->getNext()->printBlock();
        nextPtr->getPrevious()->setNext(newBlock);
        newBlock->setPrevious(nextPtr->getPrevious());
        newBlock->setNext(nextPtr);
        nextPtr->setPrevious(newBlock);
        countBlocks++;
    }
    // Удалени блока 
    void destroy(int index, indexTable& table) {
        block* nextPtr;
        int offset = table.getOffset();
        if (index < offset) index--;
        nextPtr = table.getBlockByIndex((index / offset));
        for (int i = 0; i < index % offset; i++) {
            nextPtr = nextPtr->getNext();
        }
        nextPtr->getPrevious()->setNext(nextPtr->getNext());
        delete(nextPtr);
        countBlocks--;
    }
    // Очистка всего списка 
    void clear() {
        block* nextPtr = begin, * previous;
        while (nextPtr) {
            previous = nextPtr;
            nextPtr = nextPtr->getNext();
            delete(previous);
        }
    }
    // Вывод всего листа
    void printList() {
        int ptrPos = 1;
        block* nextPtr = begin;
        cout << "/---------------PRINT LIST START---------------\\  \n\n";
        while (ptrPos <= countBlocks) {
            nextPtr->printBlock();
            cout << "|------------------NEXT BLOCK------------------|\n\n";
            nextPtr = nextPtr->getNext();
            ptrPos++;
        }
        cout << "\\---------------PRINT LIST END-----------------/ \n\n";
    }
    // Вывод конкретного блока 
    void printBlock(int index) {

    }
    // Генерация заданного кол-ва node
    void genNodes(int count, addType addType) {

        int blocksWill, addedBlocks = 0;

        block* newBlock = new block;

        if (count % newBlock->getBlockSize() == 0) blocksWill = count / newBlock->getBlockSize();
        else { blocksWill = (count / newBlock->getBlockSize() + 1); }

        for (int i = 0; i < count; i++) {
            node newNode;
            newNode.setFisrtName(fisrtNames[0 + rand() % 8]);
            newNode.setLastName(lastNames[0 + rand() % 11]);
            for (int j = 0; j < newNode.getValuesSize(); j++) {
                newNode.setValueByIndex(j, rand() % 201 - 100);
            }
            if (newBlock->getCountInBlock() == newBlock->getBlockSize()) {
                if (addType == BEGIN) addToStart(newBlock);
                else addToEnd(newBlock);
                addedBlocks++;
                newBlock = new block;
            }
            newBlock->addInBlock(newNode);
        }
        if (addedBlocks < blocksWill) {
            if (addType == BEGIN) addToStart(newBlock);
            else addToEnd(newBlock);
        }
        cout << "Сгенерированно " << blocksWill << " блоков" << endl;
    }
    // Герерация одного блока
    block* genBlock() {
        block* newBlock = new block;
        for (int i = 0; i < newBlock->getBlockSize(); i++) {
            node newNode;
            newNode.setFisrtName(fisrtNames[0 + rand() % 8]);
            newNode.setLastName(lastNames[0 + rand() % 11]);
            for (int j = 0; j < newNode.getValuesSize(); j++) {
                newNode.setValueByIndex(j, rand() % 201 - 100);
            }
            newBlock->addInBlock(newNode);
        }
        return newBlock;
    }
private:
    block* begin;
    int countBlocks = 0;
};


int main() {
    setlocale(LC_ALL, "Russian");

    list mList;
    list::indexTable mTable;
    string command;
    while (true) {
        cout << "//--Список доступных команд--//" <<
            "\n1.Генериция заданного кол-ва записей -> \tGENNODES\n2.Создание/Пересоздание индексной таблицы -> \tGENTABLE\n3.Вывод индексной таблицы -> \t\t\tPRINTTABLE" <<
            "\n4.Удаление блока -> \t\t\t\tDESTROY\n5.Вывод листа -> \t\t\t\tPRINTLIST\n6.Вывод конкретного блока -> \t\t\tPRINTBLOCK" <<
            "\n7.Очистка информационного листа -> \t\tCLEAR\n8.Выход -> \t\t\t\t\tQUIT\n\n";
        cin >> command;
        if (command == "GENNODES") {
            cout << "Введите кол-во ->\n";
            int cont;
            cin >> cont;
            cout << "Куда добавить 1 - начало 2 - конец\n";
            int addType;
            cin >> addType;
            if (addType == 1) { mList.genNodes(cont, list::BEGIN); }
            if (addType == 2) { mList.genNodes(cont, list::END); }
            continue;
        }
        if (command == "GENTABLE") {
            mTable.createIndexTable(mList);
        }
        if (command == "PRINTTABLE") {
            mTable.printTable();
            continue;
        }
        if (command == "INSERT") {
            cout << "Куда вставить индекс(int)\n";
            int index;
            cin >> index;
            mList.insert(index, mList.genBlock(),mTable);
            continue;
        }
        if (command == "DESTROY") {
            cout << "Откуда удалить индекс(int)\n";
            int index;
            cin >> index;
            mList.destroy(index,mTable);
            continue;
        }
        if (command == "PRINTLIST") {
            mList.printList(); continue;
        }
        if (command == "PRINTBLOCK") {
            cout << "Какой блок вывести индекс(int)\n";
            int index;
            cin >> index;
            mList.printBlock(index);
            continue;
        }
        if (command == "CLEAR") {
            mList.clear();
            continue;
        }
        if (command == "QUIT") return 0;
    }
    return 0;
}

Ответы

▲ 0Принят

У вас не получается поймать исключение по той причине, что вы его пытаетесь перехватить не в том месте, в котором оно вылетает. Операция присваивания не выбрасывает исключений, какой бы бред не содержался в операнде.

void setPrevious(block* previous) { 
    try{
        this->previous = previous;
    }

Исключение возникнет, когда вы попытаетесь что-то прочесть (или записать) по адресу, хранящемуся в block::previous. Поэтому блок try{} нужно делать где-то выше по иерархии. Там, где осуществляется чтение/запись. Например здесь:

try{
    addToStart( newBlock );
}
// или
try{
    addToEnd( newBlock );
}

или даже в main().
Но сразу скажу - вам это не поможет. Ну перехватите вы исключение что была попытка обратиться не в ту память - что вам это даст? Вы и так знаете, что обращаетесь не в ту память. Нужно искать ошибку и исправлять.
Ну и несколько замечаний.
Зачем вы в каждом объекте класса node храните size, если объект не меняется по размеру? Ещё и вычисляете его. И при этом используете магические цифры при объявлении массива?

class node {
    float values[7] = { 0 };
    int size;
    node() { size = sizeof(values) / sizeof(*values); }
};

Сделайте проще (и понятнее):

class node {
    static const int size = 7;
    float values[size];
};

Аналогично и в классе block

class block {
    static const int size = 2;
    node mBlock[size];

    int getBlockSize() { return size; }
};

И так же желательно сделать с массивами имен и далее в функциях использовать константы:

const int fisrtNamesSize = 8;
string fisrtNames[fisrtNamesSize] = { "Nolah","Liam","Mason","Jacob","William","Ethan","Michael","Alexander" };
const int lastNamesSize = 11;
string lastNames[lastNamesSize] = { "Williams","Peters","Gibson","Martin","Jordan","Jackson","Grant","Davis","Collins","Bradlay","Barlow" };

Поле static int blocksCount;, обозначающее количество блоков по логике должно быть не в классе block, а в классе list.
Вот здесь float values[7] = { 0 }; вы присваиваете 0 первому элементу массива, а не обнуляете весь массив.
Зачем у вас индексная таблица определяется внутри класса списка, если используется самостоятельно - отдельным объектом за пределами класса списка? В чем смысл?

class list {
public:
    // Индексная таблица
    class indexTable {};
};
int main() {
    list mList;
    list::indexTable mTable;
}

Если используете как самостоятельный объект - объявляйте как самостоятельный класс. Если хотите инкапсулировать внутри класса list - значит создавайте объект indexTable внутри объекта list и работайте с ним через функции списка.
Работа с памятью у вас идет только при создании блоков - поэтому и ищите ошибку в функциях класса list, добавляющих/удаляющих блоки.
Как минимум одна логическая ошибка есть - функция list::clear() физически удаляет все блоки из памяти, но не обнуляет счетчики в классах list и indexTable. Любая операция после вызова этой функции приведет к UB.
Ещё не совсем понятна логика работы с индексной таблицей. По-идее, она внутренний компонент и ею должен управлять список - добавлять индексы при добавлении элементов в список, перегенерировать таблицу когда нужно и т.д. А вовсе не командами пользователя. Но это уже на усмотрение разработчика.