Существуют ли временные объекты при возврате объекта из функции? - PullRequest
1 голос
/ 16 мая 2009

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

//just a sample code to support the qn
rnObj somefunction()
{
return rnObj();
}

, а также объясните, как возвращаемое значение передается вызываемой функции.

Ответы [ 4 ]

6 голосов
/ 16 мая 2009

Как можно судить по другим ответам - компилятор может оптимизировать это.

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

Взять урок -

class AClass
{
public:
   AClass( int Data1, int Data2, int Data3 );

   int GetData1();

private:
   int Data1;
   int Data2;
   int Data3;
};

При следующей тривиальной реализации -

AClass::AClass( int Data1, int Data2, int Data3 )
{
    this->Data1 = Data1;
    this->Data2 = Data2;
    this->Data3 = Data3;
}

int AClass::GetData1()
{
    return Data1;
}

И следующий код вызова -

AClass Func( int Data1, int Data2, int Data3 )
{
    return AClass( Data1, Data2, Data3 );
}

int main()
{
    AClass TheClass = Func( 10, 20, 30 );
    printf( "%d", TheClass.GetData1() );
}

(printf () добавлен только для того, чтобы компилятор не оптимизировал все ...).
В неоптимизированном коде мы ожидаем, что Func () создаст локальный AClass в своем стеке, создаст его там и скопирует в качестве возвращаемой переменной.

Однако сгенерированная сборка действительно выглядит (удаляя ненужные линии) -

_TEXT SEGMENT
___$ReturnUdt$ = 8   ; size = 4
_Data1$ = 12         ; size = 4
_Data2$ = 16         ; size = 4
_Data3$ = 20         ; size = 4

mov     eax, DWORD PTR _Data3$[esp-4]
mov     ecx, DWORD PTR _Data2$[esp-4]
mov     edx, DWORD PTR _Data1$[esp-4]
push    esi
mov     esi, DWORD PTR ___$ReturnUdt$[esp]
push    eax
push    ecx
push    edx
mov     ecx, esi
call    ??0AClass@@QAE@HHH@Z    ; AClass::AClass
mov     eax, esi
pop     esi
ret     0

3 функциональные переменные извлекаются из стека и помещаются в eax, ecx и edx.
Еще одно четвертое значение помещается в esi (и передается в ecx).
Конструктор вызывается с 3 параметрами в стеке, а ecx все еще содержит четвертое значение.

Давайте посмотрим на конструктор -

_TEXT SEGMENT
_Data1$ = 8    ; size = 4
_Data2$ = 12   ; size = 4
_Data3$ = 16   ; size = 4

mov    edx, DWORD PTR _Data2$[esp-4]
mov    eax, ecx
mov    ecx, DWORD PTR _Data1$[esp-4]
mov    DWORD PTR [eax], ecx
mov    ecx, DWORD PTR _Data3$[esp-4]
mov    DWORD PTR [eax+4], edx
mov    DWORD PTR [eax+8], ecx

ret    12   ; 0000000cH

3 параметра конструктора считываются в смещения eax - eax является копией ecx, четвертого параметра из вызова выше.
Итак, конструктор создает объект, в котором ему сообщается - четвертый параметр Func ().

И, как вы уже догадались, четвертый параметр Func () на самом деле представляет собой единственное место во всей программе, где существует созданный класс AClass. Давайте посмотрим на соответствующую часть main () -

_TEXT SEGMENT
_TheClass$ = -12    ; size = 12
_main PROC

sub    esp, 12

push   30
push   20
lea    eax, DWORD PTR _TheClass$[esp+20]
push   10
push   eax
call   ?Func@@YA?AVAClass@@HHH@Z    ; Func

12 байтов зарезервированы для класса AClass, и передаются три аргумента функции Func (), а также четвертый - указывающий на эти 12 байтов.

Это конкретный пример с конкретным компилятором. Другие компиляторы делают это по-другому. Но это дух вещей.

3 голосов
/ 16 мая 2009

Стандарт C ++ гласит, что компилятор может выбрать создание временного объекта с помощью конструктора копирования или оптимизировать временный объект.

На эту тему есть статья в Википедии с многочисленными ссылками здесь .

1 голос
/ 16 мая 2009

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

1 голос
/ 16 мая 2009

Компилятору разрешено выполнять копию по возвращении. Большинство компиляторов не будут. Однако вы должны убедиться, что rnObj имеет доступный конструктор копирования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...