c++memory-managementstack-allocation

Class type for stack allocation. Why the address for both ID instances are the same?


class ID
{
public:
    ID(const std::string& name) :
        name_(name) {}

    // explicit copy constructor as my first solution but gave me same address
    ID(const ID& other)
    { name_ = other.getName(); } 

    std::string getName() const
    { return name_; }

private:
    std::string name_;
};

ID createID(const std::string& name)
{
    ID id(name); // new stack allocation for id
    std::cout << "ID addr: " << &id << "\n";
    return id;
}

int main()
{
    ID my_id = createID("John"); // new stack allocation for my_id
    std::cout << "my_id addr: " << &my_id << "\n";
    std::cout << my_id.getName() << std::endl;
}

Platform: Ubuntu terminal (Windows' Ubuntu Subsystem)

Compilation: g++ file.cpp

Output: "Same address between IDs"

Shouldn't the output provided different stack address?

I tried replicating this with primitive integer (instead of ID class type) and it outputs different address for different instances.

int func(int i)
{
        int j = i;
        std::cout << "i addr: " << &i << std::endl;
        std::cout << "j addr: " << &j << std::endl;
        return i;
}

int main()
{
        int x = 10;

        std::cout << "X addr: " << &x << std::endl;
        int y = func(x);
        std::cout << "Y addr: " << &y << std::endl;
}

Solution

  • In this function:

    ID createID(const std::string& name)
    {
        ID id(name); // new stack allocation for id
        std::cout << "ID addr: " << &id << "\n";
        return id;
    }
    

    for the call:

    ID my_id = createID("John"); // new stack allocation for my_id
    

    it appears the compiler is doing NRVO (named return value optimization). So there is no actual copy of id in the function to the variable my_id, and no separate allocation.

    Instead this copy is elided, and you see the same address. So the comment // new stack allocation for my_id is actually incorrect.

    Note that NRVO is not guaranteed to happen, so you shouldn't rely on this behavior. The compiler could make a copy, resulting in different addresses. In fact this is what happens in the example where func returns an int. Since this is a cheap to copy type, the compiler actually makes a copy, and you see different addresses.