2013-09-08 5 views
1

Вопрос: где временный объект, выделенный при возврате значением? I.e., в стеке, в динамически распределенной памяти - что делают компиляторы в таких случаях?Если объект, возвращаемый значением, назначен

Я рыть глубоко в основной логики в ход конструктора идиомы в C++ 03, как описано в More C++ Idioms/Move Constructor, и интересная часть, как возвращается объект:

struct foo { 
    int values[100]; 
    ... 
}; 

foo func() { 
    foo ret; 
    ... // assign value to ret 
    return ret; 
} 

int main() { 
    foo a(func()); 
    ... 
    return 0; 
} 

точки между return ret; и a(func) явно содержит очистку стека и дальнейшую конструкцию копирования, но где хранится временный объект, прежде чем он будет передан в конструктор копирования? Возможно, я наблюдал за ответом в результатах поиска, но до сих пор не могу найти объяснения.

EDIT:

Хорошо, пример может показаться слишком простым. В реальных приложениях, мы могли бы написать что-то вроде этого:

class Command { 
public: 
    Command(); 
    Command(const Command &); 
    ... 

private: 
    CommandResponseType mResponseType; 

    quint64 mId; 
    QString mName; 
    quint16 mTypeCode; 
    QDateTime mDatetime; 

    TParamList mParams; 
}; 

Такой класс не POD, и компилятор, вероятно, не для оптимизации копирования конструкции Command a(someInstanceOfOtherClass.getCommand("foo")); просто выделяя значение на стеке. По крайней мере, задействуется копирование построений QString, QDateTime и TParamList.

Если я правильно понимаю, getCommand() имеет соглашение о созыве, которое обязывает вызывающего абонента самостоятельно очищать стек. Логическое решение состоит в том, чтобы выделить результат в памяти, вернуть указатель. Я попробую разные сценарии и посмотрю на сборку.

+5

Стандарт делает не говорите, где должен быть выделен этот объект, оставив его компилятору для принятия решения о реализации. Разработчики компилятора могли бы принять решение о возврате возвращаемого значения различными способами - от использования регистров, когда объект подходит в них, к использованию стека, чтобы потребовать, чтобы вызывающий передал ссылку на объект, в который должно быть скопировано возвращаемое значение. – dasblinkenlight

+3

@dasblinkenlight (и стандарт даже не связан с такими вещами, как «стек» или «куча».) –

+0

Именно поэтому я спросил о таких зависимых от компилятора функциях. «Требование, чтобы вызывающая сторона передавала ссылку на объект, в который должно быть скопирована возвращаемое значение», является для меня чем-то новым. –

ответ

0

простой пример того, как объект может быть построен на стек неявное использование operator new в не-выделяя форму, void* operator new (std::size_t size, void* ptr) throw():

class Foo { 
public: 
    Foo(); 
    Foo(const Foo &); 
    ~Foo(); 
private: 
    // data members 
    ... 
} 

void bar() { 
    Foo f1; 
    // // Allocate sufficient memory on stack: 
    // char f1[sizeof(Foo)]; 
    // // Call operator new on this memory and execute Foo's constructor 
    // // with (this == f1): 
    // operator new (sizeof(Foo),f1) Foo(); 
    ... 

    // Or «non-optimized» case, with copy-constructor involved: 
    Foo f2 = Foo(); 
    // char f2[sizeof(Foo)]; 
    // { 
    //  // tmp is constructed as in the previous case: 
    //  char tmp[sizeof(Foo)]; 
    //  operator new (sizeof(Foo),tmp) Foo(); 
    //  // And then f2 is constructed using Foo's copy-constructor: 
    //  Foo::Foo(f2 /*this*/, tmp) 
    // } 
    // // Here, tmp must be destructed and popped from stack. 
} 

Ссылки:

http://en.wikipedia.org/wiki/Return_value_optimization

1

Одна из возможностей заключается в отсутствии временного объекта, если компилятор достаточно умен, чтобы оптимизировать его в распределении (внутри фрейма стека main()) и перемещении. Если есть, это, скорее всего, и в стеке, но почему бы вам не проверить сгенерированную сборку?

+1

Пришло время сделать это. –

0

Когда я пишу foo a(func());, вызывается конструктор struct, который является в основном функцией. Это принимает аргумент func(). Таким образом, код сначала вычислит func() и вытолкнет возвращаемое значение в стек. Теперь foo() примет это значение в качестве аргумента. И поставщики внедрения могут свободно делать любую оптимизацию здесь. Pls Исправьте меня, если я ошибаюсь.

+1

В более сложных сценариях это не обязательно, просто. См. Редактирование. –