Использование std :: stringstream в std :: exception.what () - PullRequest
0 голосов
/ 08 января 2020

Желая реализовать класс std :: exception и особенно функцию what, я столкнулся с проблемой с std :: stringstream.

Чтобы сделать его коротким, он печатает пустую строку, когда я использую эту реализацию:

const char *what() const throw () {
   std::stringstream ss;

   ss << "Error";
   if (_line > -1)
      ss << " at line " << std::to_string(_line);

   ss << ": " << _msg;

   return ss.str().c_str();
}

и что-то печатать (например: «Ошибка в строке 3»), когда я использую эту реализацию:

const char *what() const throw () {
   std::stringstream ss;

   ss << "Error";
   if (_line > -1)
      ss << " at line " << std::to_string(_line);

   //ss << ": " << _msg; // COMMENT THIS LINE

   return ss.str().c_str();
}

Затем я даже пытаюсь увидеть, происходит ли это из: "или из переменной _msg (std :: string), но, похоже, ни одно из них не имеет влияния.

Это проблема буфера со стороны потока строк или проблема манипуляции с моей стороны? Если это проблема манипуляции, пожалуйста, объясните мне, как ее использовать.

Ответы [ 2 ]

2 голосов
/ 08 января 2020

ss.str().c_str();

str() делает это:

Возвращает копию базовой строки [..]

(Источник)

Итак, теперь у вас есть временный std::string, на который вы звоните c_str(). Это возвращает указатель на (C) строку , управляемую этим временным std::string.

Так что, как только этот оператор закончится (в этом случае, когда функция возвращает), временный std::string будет уничтожен, что приведет к удалению / освобождению памяти, на которую указывает указатель, полученный с c_str().

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

Решения:

  1. Сделайте копию строки C и верните ее. Плохо , потому что теперь вызывающая сторона должна освободить эту копию, а это не является частью "API" из std::exception::what()

  2. Добавить std::string члена вашего класса, заполните , что , возвращаемым значением str() (предпочтительно в конструкторе класса ...) и верните, что члены внутренние C string:

    struct my_exception : public std::exception {
      std::string description;
      my_exception() {
        std::stringstream ss;
        // code to fill ss
        description = ss.str();
      }
      char * what() const override {
        return description.c_str();
      }
    };
    

Сохранение важного комментария от грецкого ореха:

Вы должны быть осторожны с этим, хотя. std::string может выдать при копировании, и если это произойдет, например, с обработчиком исключения, перехваченным при копировании, будет вызван std::terminate и программа будет прервана. С другой стороны, он, вероятно, будет выдавать только при сбое выделения памяти, что может быть ошибкой, которую вы все равно считаете неисправимой (или нет).

1 голос
/ 08 января 2020

Функция str возвращает строку по значению. Когда вы используете это значение, это значение будет временным, и строковый объект будет уничтожен сразу после окончания выражения (ss.str().c_str()).

То, что временный объект уничтожен, означает, что возвращаемый вами указатель станет немедленно недействителен.

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

...