2016-06-21 2 views
7

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

#include <string> 
#include <iostream> 
#include <cstdio> 

std::string foo() { 
    return "HELLO"; 
} 

void bar(const char *p) { 
    std::printf("%s\n", p); 
} 


int main() { 

    // FIRST CASE: 
    // I know this is bad, because after the assignment 
    // the variable returned by foo() is destroyed and we 
    // have a bad reference. 
    const std::string &s = foo(); 
    bar(s.c_str()); 


    // SECOND CASE: 
    // But what about that ? I don't know exactly if the 
    // object is alive after the call to c_str() 
    bar(foo().c_str()); 

    return 0; 
} 

выход GCC в обоих случаях «HELLO», но я думаю, что это потому, что это не очистка сырой памяти.

Во втором случае, когда именно временный объект уничтожен?

+0

Небольшая заметка для будущего интереса - Valgrind - это правильный инструмент для демонстрации ошибок после использования (и многих других). Как вы подозреваете, просто запуск кода, чтобы увидеть, если он работает, не обязательно расскажет вам об ошибках! –

ответ

10

Фактически, оба эти случая в порядке.

В первом случае привязка временного к значению const продлевает срок его службы до s. Поэтому он не будет уничтожен до выхода main.

Во втором случае временное уничтожается после окончания его полного выражения. В этом случае это вызов функции. Если вы сохранили эту строку C в любом месте, из которой вышло bar, а затем попытались получить к ней доступ, то вам назначена дата с неопределенным поведением.

+0

'В первом случае привязка временной к константной ссылке продлевает срок ее службы до значения s. Таким образом, он не будет уничтожен до основных выходов. Интересно, вы случайно узнали, где в спецификации это обсуждается? –

+0

Где в спецификации говорится о const? : o спасибо. – FrameBuffer

+1

@GillBates Он запускается [здесь] (http://eel.is/c++draft/class.temporary#4) (C++ 17 Draft) – NathanOliver

2

Оба эти случая четко определены. Чтобы увидеть проблемное дело, не хранить результат c_str() только после std::string разрушается:

#include <string> 
#include <cstdio> 

std::string foo() { 
    return "HELLO"; 
} 

void bar(const char *p) { 
    std::printf("%s\n", p); 
} 

int main() { 
    { 
     // FIRST CASE: 
     // This is okay, because the reference is const, so the object is alive 
     // until s goes out of scope. 
     const std::string &s = foo(); 
     bar(s.c_str()); 
    } 

    { 
     // VARIANT FIRST CASE: 
     // This is bad; the pointer is dangling 
     const char *s = foo().c_str(); 
     bar(s); 
    } 

    { 
     // SECOND CASE: 
     // Is the object still alive after the call to c_str()? Yes, it's alive 
     // until after bar() has returned. 
     bar(foo().c_str()); 
    } 

    return 0; 
} 

Когда я запускаю это под Valgrind, он дает ошибки только для варианта случая (с const char *s):

==9981== Invalid read of size 1 
==9981== at 0x4C2E0E2: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==9981== by 0x543EC7B: puts (ioputs.c:36) 
==9981== by 0x400937: bar(char const*) (37946437.cpp:9) 
==9981== by 0x4009AA: main (37946437.cpp:25) 
==9981== Address 0x5aabcf8 is 24 bytes inside a block of size 30 free'd 
==9981== at 0x4C2C2BC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==9981== by 0x4F058FD: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.22) 
==9981== by 0x40099E: main (37946437.cpp:24) 
Смежные вопросы