Разыменование нулевого указателя C++ при добавлении экземпляра в std::map внутри конструктора

Я пытаюсь получить список кодов ошибок для своего приложения на C++, чтобы ошибки распространялись на несколько проектов. Каждая ошибка содержит просто уникальный код (который можно передать exit() и подобным) и сообщение для отображения. Помимо конструктора и некоторых методов приведения, мне также нужна какая-то функция для получения ошибки по ее коду, чтобы при выходе из программы A я мог прочитать код возврата из программы B и отобразить его пользователю/журналу/что угодно. Это подразумевает, очевидно, отслеживание всего списка ошибок. Для этого я сохраняю статический std::unordered_map внутри класса Error, чтобы конструктор Error добавлял к нему создаваемый экземпляр. Однако по какой-то причине, которую я не совсем понимаю, я продолжаю получать ошибку разыменования нулевого указателя при добавлении Error на карту. Мой код выглядит следующим образом:

error_codes.h

namespace ErrorCodes{
    class Error{
    private:
        int code;
        std::string message;

        static int counter;

        static std::unordered_map<int, Error*> allErrors;

        Error(int code, std::string message);

    public:
        Error(): code(1), message(""){}

        explicit Error(std::string message);

        Error getByCode(int code);

        operator int() const{
            return code;
        }

        operator std::string() const{
            return message;
        }

        std::ostream& operator<<(std::ostream& os)
        {
            os << std::string("ERR_")+std::to_string(code)+std::string(": ") + message;
            return os;
        }

        operator QString() const{
            return QString::fromStdString(message);
        }

        bool operator == (Error other){
            return other.code == code;
        }


    };

    //List all errors here
    const Error ERR_ERROR_CODE_NOT_FOUND("Couldn\'t find error with code %1"); //Follow QString formatting style for more complex messages
    const Error ERR_GLOBAL_ID_NOT_FOUND("Problem with the global Id, identifier not found.");
    const Error ERR_DIR_NOT_FOUND_OR_CREATED("Dir not found or created, check configuration.");
}

а затем мой error_codes.cpp:


using namespace ErrorCodes;

int Error::counter = 0;
std::unordered_map<int, Error*> Error::allErrors = std::unordered_map<int, Error*>();

Error::Error(int code, std::string message) : code(code), message(message){
    std::pair pair = std::make_pair(code, this);
    allErrors.emplace(pair); //It fails here
}

Error::Error(std::string message){
    int code = --counter;
    Error(code, message);
}

Error Error::getByCode(int code){
    auto it = allErrors.find(code);
    if (it!=allErrors.end()){
        return *(it->second);
    }else{
        qCritical() << QString(ERR_ERROR_CODE_NOT_FOUND).arg(code);
        exit(ERR_ERROR_CODE_NOT_FOUND);
    }
}

PS: В идеале я бы Error создавал не класс, а структуру и имел бы какую-то коллекцию (например, массив), которая содержала бы все ошибки и позволяла бы мне получать их по коду ошибки. Однако я не нашел способа сделать ошибки доступными в массиве И как «константу» (чтобы в любом месте моего кода я мог просто написать ErrorCodes::XXX) без необходимости вручную создавать массив со всеми Error (например, , для этой цели идеально подойдет перечисление). Я был бы признателен, если бы кто-нибудь еще знал лучший способ сделать это.

почему карта хранит указатели? Почему бы и нет std::unordered_map<int, Error> ? А ваш PS неясен. Нет никакой разницы между структурами и классами (struct и class — два ключевых слова для определения типа класса (с разным доступом по умолчанию))

463035818_is_not_an_ai 22.04.2024 13:19

пожалуйста, попробуйте создать минимально воспроизводимый пример

463035818_is_not_an_ai 22.04.2024 13:20
Error(code, message); в вашем конструкторе Error::Error(std::string message) не делает то, что вы думаете. Если только вы не хотели создать экземпляр Error, который немедленно выбрасывается, оставляя висячий указатель на карте ошибок...
Botje 22.04.2024 13:24

В дополнение к тому, что упомянул Ботье, вы хотите, чтобы ваши переменные static инициализировались до глобальных экземпляров Error.

Ted Lyngmo 22.04.2024 13:30

@ 463035818_is_not_an_ai Он сохраняет ошибку*, потому что я (ошибочно) подумал, что ошибка может возникнуть из-за попытки разыменования this внутри конструктора. Что касается PS, я на самом деле имел в виду, что мне не понадобится «умный» конструктор, заполняющий карту, а просто простая структура данных для хранения кода и сообщения. Я отредактирую вопрос.

Romolo Caponera 22.04.2024 13:31

Следует отметить, что сначала необходимо создать «фиктивную» ошибку, чтобы получить ошибку «по коду», — это неуклюжий интерфейс. Сделайте getByCode статичным. Также не имеет особого смысла иметь ошибку «по умолчанию».

molbdnilo 22.04.2024 13:40
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
81
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Похоже, вы используете неправильный синтаксис конструктора делегата.

Error::Error(std::string message){
    int code = --counter;
    Error(code, message); //temporary
}

Должно быть

Error::Error(std::string message) : Error(--counter, message) {}

В противном случае вы создаете временную ошибку, которая существует только в кадре стека конструктора делегата. При выходе из конструктора указатель «this» временной ошибки указывает на недопустимую область памяти.

Error::Error(int code, std::string message) : code(code), message(message){
    std::pair pair = std::make_pair(code, this); // <- as soon as the function exits, "this" will be an invalid pointer
    allErrors.emplace(pair);
}

Я не совсем понимаю, почему, используя только этот код, вы получаете ошибку сегментации напрямую, а не при попытке разыменовать ошибку с карты, но это хорошее место для начала.

Ответ принят как подходящий

В настоящее время вы создаете экземпляры Error до инициализации unordered_map. Инициализируйте переменные static перед их использованием. Пример:

Заголовок:

namespace ErrorCodes {
// class definition

// List all errors here
extern const Error ERR_ERROR_CODE_NOT_FOUND;
extern const Error ERR_GLOBAL_ID_NOT_FOUND;
extern const Error ERR_DIR_NOT_FOUND_OR_CREATED;
}  // namespace ErrorCodes

.cpp файл:

namespace ErrorCodes {

// Initialize the static variables:
int Error::counter = 0;
std::unordered_map<int, Error*> Error::allErrors{};

// Create all the errors afterwards:
const Error ERR_ERROR_CODE_NOT_FOUND("Couldn\'t find error with code %1");
const Error ERR_GLOBAL_ID_NOT_FOUND("Problem with the global Id, identifier not found.");
const Error ERR_DIR_NOT_FOUND_OR_CREATED("Dir not found or created, check configuration.");
//...
}  // namespace ErrorCodes

Также обратите внимание, что делегирование конструктора должно использовать список инициализаторов членов.

Пример:

Error::Error(int code, std::string message) : code(code), message(std::move(message)) {
    allErrors.emplace(code, this);  // It doesn't fail here anymore
}

Error::Error(std::string message) : Error(--counter, std::move(message)) {}

Альтернативно, определите переменные static и константы встроенными.

Заголовок:

namespace ErrorCodes {
class Error {
private:
    inline static int counter = 0;
    inline static std::unordered_map<int, Error*> allErrors{};

    //...
};

// List all errors here
inline const Error ERR_ERROR_CODE_NOT_FOUND("Couldn\'t find error with code %1");
inline const Error ERR_GLOBAL_ID_NOT_FOUND("Problem with the global Id, identifier not found.");
inline const Error ERR_DIR_NOT_FOUND_OR_CREATED("Dir not found or created, check configuration.");

}  // namespace ErrorCodes

Итак, чтобы создать экземпляр статики перед кодами ошибок, мне нужно будет использовать ключевое слово extern и впоследствии определить их в cpp... Есть ли способ объявить/определить их в одном месте?

Romolo Caponera 22.04.2024 13:43

@RomoloCaponera Да, я только что добавил для этого опцию.

Ted Lyngmo 22.04.2024 13:44

Другие вопросы по теме