简体   繁体   中英

Confusion on operator overloading and heap vs stack

I'm looking at the following tutorial: http://www.videotutorialsrock.com/opengl_tutorial/animation/home.php

This person has a vector class:

class Vec3f {
private:
    float v[3];
public:
    Vec3f();
    Vec3f(float x, float y, float z);

    float &operator[](int index);
    float operator[](int index) const;

    Vec3f operator*(float scale) const;
    Vec3f operator/(float scale) const;
    Vec3f operator+(const Vec3f &other) const;
    Vec3f operator-(const Vec3f &other) const;
    Vec3f operator-() const;

    const Vec3f &operator*=(float scale);
    const Vec3f &operator/=(float scale);
    const Vec3f &operator+=(const Vec3f &other);
    const Vec3f &operator-=(const Vec3f &other);

    float magnitude() const;
    float magnitudeSquared() const;
    Vec3f normalize() const;
    float dot(const Vec3f &other) const;
    Vec3f cross(const Vec3f &other) const;
};

With an example definition:

Vec3f Vec3f::operator*(float scale) const {
    return Vec3f(v[0] * scale, v[1] * scale, v[2] * scale);
}

I'm confused on why this works. Shouldn't this immediately cause a segmentation fault? The return value is on the stack and should get deleted upon termination of all of these functions. Why does it work? Is my understanding of stack vs heap incorrect?

EDIT: I am basing my understanding from this: How to return a class object by reference in C++?

Vec3f Vec3f::operator*(float scale) const {
    return Vec3f(v[0] * scale, v[1] * scale, v[2] * scale);
}

This uses return by value, so what is returned is the value of the class instance created by that line, not the instance itself.

This is fundamentally no different from return 1; . The value one is returned, not any particular instance or class member that contains that value. As with pretty much everything else, it's the implementation's responsibility to figure out how to accomplish what the code asks for -- in this case, ensuring that some instance exists to hold the returned value with an appropriate lifetime.

You can look in the following example:

Vec3f Vec3f::operator*(float scale) const {
    return Vec3f(v[0] * scale, v[1] * scale, v[2] * scale);
}

Vec3f a(1,2,3);
Vec3f b;
b = a * 2;

In general the following will happen:

  1. the operator overload implementation will construct a new instance of the Ve3f with the new arguments, representing multiplication.

  2. the return procedure will call a default copy constructor of b with the constructed object in the argument. The copy-constructor will copy fields from its argument to the instance of 'b'.

You can always implement your own copy constructor to do something else besides the shallow copy which the default one provides.

Vec3f(const Ver3f &src)...

So, as a result you will get a new objects with all fields copied from the one created at the return statement. This is the return by value as defined in c++ for objects.

The result would be different if you returned the object by a pointer or a reference. It will cause a memory corruption.

That's the binary multiplication operator, and it's returning a copy of the data in the Vec3f instance, multiplied by float scale , into an rvalue for use by the rest of an expression.

How that works is already answered in What are rvalues, lvalues, xvalues, glvalues, and prvalues?

Also see https://en.cppreference.com/w/cpp/language/operator_arithmetic

So, every cpu has it's own calling convention. For more detailed info look into this and this

Basically, the return value, or the address to the return value is copied to the a register like R0 in ARM and EAX in x86 so the caller of the function can access it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM