0

I'm trying to test how and where the data is located and destroyed. For the example code below:

  1. A new point is created and returned with NewCartesian method. I know that it should be stored in heap. When it's pushed back into the vector, does the memory of the points content is copied into a new Point structure? Or is it stored as a reference the this pointer?

  2. When the point is created, when is it destroyed? Do I need to destroy the points when I'm done with it? Or are they destroyed when they are not usefuly anymore? For example, if main was another function, the vector would not be useful when it's finished.

  3. Depending on the answers above, when is it good to use the reference of objects? Should I use Point& p or Point p for the return of Point::NewCartesian?

    #define _USE_MATH_DEFINES
    
    #include <iostream>
    #include <cmath>
    
    using namespace std;
    
    struct Point {
    private:
      Point(float x, float y) : x(x), y(y) {}
    
    public:
      float x, y;
    
      static Point NewCartesian(float x, float y) {
        return{ x, y };
      }
    };
    
    int main()
    {
      vector<Point> vectorPoint;
      for (int i = 0; i < 10000; i++) {
        Point& p = Point::NewCartesian(5, 10);
        vectorPoint.push_back( p );
        // vectorPoint.push_back( Point::NewCartesian(5, 10) );
    
        Point& p2 = Point::NewPolar(5, M_PI_4);
      }
    
      cout << "deneme" << endl;
    
      getchar();
    
      return 0;
    }
    

Thank you for your help,

Cheers,

trincot
  • 317,000
  • 35
  • 244
  • 286
MeCe
  • 445
  • 2
  • 5
  • 12

2 Answers2

4

... I know that it should be stored in heap.

Firstly, please read this explanation of why it's preferable to talk about automatic and dynamic object lifetimes, rather than stack/heap.

Secondly, that object is neither dynamically-allocated nor on the heap. You can tell because dynamic allocation uses a new-expression, or a library function like malloc, calloc or possibly mmap. If you don't have any of those (and you almost never should), it's not dynamic.

You're returning something by value, so that thing's lifetime is definitely automatic.

When the point is created, when is it destroyed?

If you write the full set of copy/move constructors and assignment operators, plus a destructor, you can simply set breakpoints in them in the debugger and see where they get invoked. Or, have them all print their this pointer and input arguments (ie, the source object being moved or copied from).

However, since we know the object is automatic, the answer is easy - when it goes out of scope.

Should I use Point& p or Point p for the return of Point::NewCartesian?

Definitely the second: the first returns a reference to an object with automatic lifetime in the scope of the NewCartesian function, meaning the objected referred to is already dead by the time the caller gets the reference.

Finally, this code

Point& p = Point::NewCartesian(5, 10);

is weird - it makes it hard to determine the lifetime of the Point referred to by p by reading the code. It could be some static/global/other object with dynamic lifetime to which NewCartesian returns a reference, or (as is actually the case) you could be binding a reference to an anonymous temporary. There's no benefit to writing it this way instead of

Point p = Point::NewCartesian(5, 10);

or just passing the temporary straight to push_back as in your commented code.


As an aside, the design of Point is very odd. It has public data members, but a private constructor, and a public static method that just calls the constructor. You could omit the constructor and static method entirely and just use aggregate initialization, or omit the static method and make the constructor public.

Useless
  • 64,155
  • 6
  • 88
  • 132
3

1a. No, it's on the stack, but read the answer by Useless, why the terms stack and heap are not the best choice.

1b. It gets copied when you call push_back.

2 . It's destroyed immediately after being created, because it only exists within the scope of the NewCartesian call and for the duration of the return being evaluated.

3a. You use a reference whenever you have a valid instance and want to pass it to a function without creating a copy. Specifically the function should have a reference parameter.

3b. You should use Point p, not Point& p, because right now you get a dangling reference to an object that doesn't exist anymore (see 2.)

As pointed out by Steven W. Klassen in the comments, your best option is the code that you have commented out: vectorPoint.push_back( Point::NewCartesian(5, 10) );. Passing the call to NewCartesian directly into push_back without making a separate local copy, allows the compiler to optimize it so that the memory is constructed exactly where push_back wants it and avoiding any intermediate memory allocations or copies. (Or more technically, it allows it to use the move operator.)

Max Vollmer
  • 8,412
  • 9
  • 28
  • 43
  • 1
    I would add to this that your best option is the code that you have commented out. Namely passing NewCartesian directly into the push_back without making a separate local copy. This can allow the compiler to optimize it so that the memory is constructed exactly where push_back wants it and avoiding the copy. (Or more technically, it allows it to use the move operator.) – Steven W. Klassen Sep 03 '18 at 15:21
  • Well, this makes sense perfectly now. Thank you guys. – MeCe Sep 03 '18 at 15:27