Object Return Semantics in C++

Analysis of Return Object Functions

Test Code Analysis

To observe copy constructor behavior, we define a custom class with instrumentation:

class TestObject {
    int data;
    
public:
    TestObject() {
        printf("0x%p Constructor called\n", this);
    }
    
    TestObject(TestObject& source) {
        printf("0x%p Copy constructor from 0x%p\n", this, &source);
    }
    
    ~TestObject() {
        printf("0x%p Destructor called\n", this);
    }
};

TestObject createObject() {
    TestObject localObj;
    return localObj;
}

int main() {
    printf("=== Test Case 1 ===\n");
    TestObject obj1 = createObject();
    printf("obj1 address: 0x%p\n\n", &obj1);
    
    printf("=== Test Case 2 ===\n");
    TestObject obj2;
    printf("obj2 address: 0x%p\n", &obj2);
    obj2 = createObject();
    printf("\n");
    
    printf("=== Test Case 3 ===\n");
    createObject();
    printf("\n");
    
    return 0;
}

Object Initialization Behavior

Test Case 1: TestObject obj1 = createObject();

When initializing an object with a return value, the compiler passes obj1's adress implicit to createObject(). The return statement invokes the copy constructor to initialize obj1 directly, avoiding separate construction.

Test Case 2: obj2 = createObject();

For assignment operations, the compiler generates a temporary object. createObject() initializes this temporary, which is then assigned to obj2 through member-wise copying (shallow copy). This approach differs from copy construction.

Test Case 3: createObject();

Standalone functon calls generate temporary objects that are destroyed immediately after the expression completes.

Assembly-Level Implementation

Function Implementation:

createObject:
    lea     ecx, [ebp-0x10]    ; localObj construction
    call    TestObject::TestObject
    mov     dword ptr [ebp-4], 1
    
    lea     eax, [ebp-0x10]    ; load localObj address
    push    eax                 ; pass as source
    mov     ecx, [ebp+8]       ; get target address parameter
    call    TestObject::TestObject(TestObject&) ; copy construct
    
    lea     ecx, [ebp-0x10]    ; destroy localObj
    call    TestObject::~TestObject
    mov     eax, [ebp+8]       ; return target address

Call Site Analysis:

; Case 1: Direct initialization
lea     eax, [ebp-0x10]        ; obj1 address
push    eax                     ; implicit parameter
call    createObject

; Case 2: Assignment operation
lea     eax, [ebp-0x1C]        ; temporary object address  
push    eax
call    createObject
mov     [ebp-0x28], eax        ; store returned address
mov     ecx, [ebp-0x28]        ; member-wise copy
mov     edx, [ecx]
mov     [ebp-0x14], edx
lea     ecx, [ebp-0x1C]        ; destroy temporary
call    TestObject::~TestObject

The compiler employs different strategies based on usage context: direct construction for initialization versus temporary generation for assignment operations.

Tags: C++ Object Return Copy Constructor Compiler Optimization

Posted on Wed, 01 Jul 2026 17:49:27 +0000 by bapi