C ++, std :: ofstream, исключение - PullRequest
       7

C ++, std :: ofstream, исключение

1 голос
/ 05 августа 2011

Что не так в этом коде и как это исправить?

int _tmain(int argc, _TCHAR* argv[])
{
std::ostream * o = &std::cout;
char text[4096];
char *file = "D://test.txt";

if ( file != NULL ) 
{
  strcpy ( text, file );
  strcat ( text, ".log" );
  o = & std::ofstream ( text );
}
*o << "test"; //Exception
return 0;
}

Ответы [ 6 ]

4 голосов
/ 05 августа 2011
o = & std::ofstream ( text );

Правое выражение создает временное и вы получаете адрес временного, который уничтожается в конце выражения.После этого использование o вызовет неопределенное поведение.

Вы должны сделать это:

{
   //...
   o = new std::ofstream ( text );
   if ( *o )
        throw std::exception("couldn't open the file");
}
//...

if  ( o != &std::cout )
   delete o; //must do this!
2 голосов
/ 05 августа 2011

не должен компилироваться; выражение std::ofstream( text ) является rvalue (временный), и C ++ не позволяет вам взять адрес (оператор &) временный. И время жизни только временное до конца полного выражения, поэтому его деструктор будет называться (и память, в которой он находится, может быть использована для других целей), как только Вы передаете ; в конце выписки.

Простое создание ofstream именованной локальной переменной тоже не поможет, поскольку время жизни переменной только до конца блока в который был объявлен (следующий }). Вы должны определить std::ofstream перед if, откройте его и установите o в if, e.g.:

std::ofstream mayOrMayNotBeUsed;
if ( file != NULL ) {
    //  ...
    mayOrMayNotBeUsed.open( text );
    if ( !mayOrMayNotBeUsed.is_open() ) {
        //  Do something intelligent here...
    }
    o = &mayOrMayNotBeUsed;
}
1 голос
/ 05 августа 2011

Это

o = & std::ofstream ( text );

создает временный объект, o начинает указывать на адрес этого объекта и позже (сразу после выполнения этой строки) объект уничтожается. Таким образом неопределенное поведение (при разыменовании недопустимого указателя).

Решение - создайте его с помощью new:

o = new std::ofstraem( text );

НО не забудьте освободить выделенную память до return:

*o << "test";

if  ( &std::cout != o  ) // don't forget the check .. as I did at the first time
{
    o->close();  // not absolutely necessary, 
             // as the desctructor will close the file
    delete o;
}
return 0;
1 голос
/ 05 августа 2011

o = & std::ofstream ( text ); это создает временный объект ofstream, адрес которого назначен на o, но объект мгновенно уничтожается, поэтому o указывает на удаленный объект. Это должно работать (с использованием статического):

int _tmain(int argc, _TCHAR* argv[])
{
    std::ostream * o = &std::cout;
    char text[4096];
    char *file = "D://test.txt";

    if ( file != NULL ) 
    {
        strcpy ( text, file );
        strcat ( text, ".log" );
        static std::ofstream myofstream( text );
        o = &myofstream;
    }
    *o << "test"; //Exception
    return 0;
}
1 голос
/ 05 августа 2011

Проблема в том, что этот код приводит к неопределенному поведению:

o = & std::ofstream ( text );

Когда вы пишете

std::ofstream ( text )

Это создает временный ostream объект, срок жизни которого заканчивается, как только завершается выполнение оператора, в котором он находится. Когда вы берете его адрес и назначаете его указателю o, указатель теперь указывает на временный объект, срок жизни которого заканчивается. Как только оператор завершит выполнение, o теперь указывает на объект, срок жизни которого истек, поэтому использование этого объекта имеет неопределенное поведение. Следовательно, когда вы пишете

*o << "test";

Вы пытаетесь выполнить операцию на мертвом объекте, вызывая проблемы.

Чтобы исправить это, вы должны либо

  1. Динамически распределить ofstream, написав o = new std::ofstream(text);, который создает объект так, что его время жизни продолжается после конца оператора, или
  2. Объявите std::ofstream в верхней части _tmain, чтобы его время жизни распространялось на всю оставшуюся функцию.

Надеюсь, это поможет!

0 голосов
/ 05 августа 2011

Боюсь, вы смешиваете C и C ++ очень нездоровым образом.

Во-первых, я искренне рекомендую использовать std::string вместо char*, поверьте, у вас будет гораздо меньше проблем.

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

Я бы предложил следующий код:

void execute(std::ostream& out) {
  out << "test\n";
} // execute

int main(int argc, char* argv[]) {
  if (argc == 1) {
    execute(std::cout);
    return 0;
  }

  std::string filename = argv[1];
  filename += ".log";

  std::ofstream file(filename.c_str());
  execute(file);
  return 0;
}

Что показывает, как избежать двух ловушек, в которые вы попали:

  • используя std::string Я избегаю выделять буфер статического размера, и поэтому я рискую переполнить буфер. Кроме того, операции намного проще.
  • используя функцию для подъема логики печати, я покончу с указателем и появившимися в нем тонкими проблемами.

К сожалению, std::string и std::fstream (и супруги) в настоящее время смешиваются не так хорошо. Исторический дефект ... исправлен в C ++ 0x, если я правильно помню.

...