Pointer question

Hi,
I'm having problem understanding code below. To be specific, I can't understand the relationship between line 7 and line 14.

SO, pointer c points to function CreateUnitCube?? How's that gonna work..
And, class Cube has a pointer of CreateUnitCube? What does it point to?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
double someOtherFunction();  // Forward decl

#include <iostream>
#include "Cube.h"
using uiuc::Cube;

Cube *CreateUnitCube() {
  Cube cube;
  cube.setLength(15);
  return &cube;
}

int main() {
  Cube *c = CreateUnitCube();
  someOtherFunction();
  double a = c->getSurfaceArea();
  std::cout << "Surface Area: " << a << std::endl;
  double v = c->getVolume();
  std::cout << "Volume: " << v << std::endl;
  return 0;
}


// Some other function that does something that uses some stack memory.
// In this code, we calculate the total volume of cubes of length from 0..99.
double someOtherFunction() {
  Cube cubes[100];
  double totalVolume = 0;

  for (int i = 0; i < 100; i++) {
    cubes[i].setLength(i);
    totalVolume += cubes[i].getVolume();
  }

  return totalVolume;
}
Last edited on
I think we might be able to call it an object factory, though it's not quite that complex... In all this function is not currently very useful except as a demonstration of objects, pointers, and scope.

CreateUnitCube() will create the cube object (I'm assuming with some kind of dynamic memory otherwise it will go out of scope). The function then sets a default length of 15. Finally it returns the address to that cube so it can be held by a pointer in the calling function.

As far as OOP programming, this could have just been made with a constructor instead with a default value, but it might be useful if you don't have access to the library's source code...

Edit: Nope, Ganado is right. It goes out of scope even if the object contains dynamic memory. The object would have to be created dynamically as well... see below.
Last edited on
Your program is illegal, so you're engendering undefined behavior.

No, the pointer doesn't point to a function, it's pointing to the local variable Cube cube. The problem is, once the function's scope ends, that local object no longer exists, so a pointer to it is pointing to garbage. You should not be using this function.

Just return by value unless you have a specific reason not to.
1
2
3
4
5
Cube CreateUnitCube() {
  Cube cube;
  cube.setLength(15);
  return cube;
}

Why would a unit cube have a length of 15, by the way? That seems fishy to me.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
double someOtherFunction();  // Forward decl

#include <iostream>
#include "Cube.h"
using uiuc::Cube;

Cube *CreateUnitCube() {
  Cube * cube = new Cube;
  cube->setLength(15);
  return cube;
}

int main() {
  Cube *c = CreateUnitCube();
  someOtherFunction();
  double a = c->getSurfaceArea();
  std::cout << "Surface Area: " << a << std::endl;
  double v = c->getVolume();
  std::cout << "Volume: " << v << std::endl;
  delete c;
  return 0;
}


// Some other function that does something that uses some stack memory.
// In this code, we calculate the total volume of cubes of length from 0..99.
double someOtherFunction() {
  Cube cubes[100];
  double totalVolume = 0;

  for (int i = 0; i < 100; i++) {
    cubes[i].setLength(i);
    totalVolume += cubes[i].getVolume();
  }
  
  return totalVolume;
}


Again, this is not a recommended way of doing things, but might have niche use cases. One reason I needed a similar function to this was in implementing a plugin system. One method of creating plugins can utilize a C style function call that returns a pointer to a dynamically created object. (Note that in such a case the delete call should be another function call from that dynamic library rather than calling delete directly in main).

Edit: Thank you sir doug4. I'm always half asleep while posting here, thanks for the save.
Last edited on
@newbieg,

Line 9 needs to be cube->setLength(15); because "cube" is a pointer.
Just to be clear, the line:

Cube *c = CreateUnitCube();

is just the calling code receiving the return value from the CreateUnitCube() function and storing it in a variable. It's exactly the same as any other time when a function returns a value. A pointer is a type of variable, like any other type.

you can also make the local variable static and return its address, this can give you performance lifts (its a simple way to avoid copying things) in small helper functions as long as you keep in mind that every time you call it you get the SAME item. Its probably wrong to do that here, but there are times when you may want to do it.
Does style affect thinking?
1
2
3
Cube  *CreateUnitCube();
Cube * CreateUnitCube();
Cube*  CreateUnitCube();

Three examples that differ only by whitespace. All three lines are effectively identical.
There is usually debate whether first or last is "most descriptive".

The line is a declaration. There are parentheses (), so this declares a function.
Function declaration has at least:
returntype name () ;
In this case the returntype, the type of the value that the function returns, is Cube *. Pointer to Cube. An address of some object.
The name is CreateUnitCube.

The Cube *c; is a declaration of a variable. Type of variable is pointer to Cube.
There is initializer too, so the initial value of c is known.
1
2
Cube *c = CreateUnitCube() ;
Cube *c { CreateUnitCube() };

Again, both lines do the same. The latter syntax (with {}) has been available since C++11.
When the variable c is created, function CreateUnitCube is called and whatever value is returned by the function call will be stored in c.
people here seem to favor
Cube *c { CreateUnitCube() };
but to me it is cluttery here. There are times (esp in inheritance or containers) where it is awesome, but for ints and such I find = more readable and one less letter to type. Styles vary :)
Using {} for initialisation of integers and the like has one advantage:
it prevents inadvertent narrowing conversions.
https://www.stroustrup.com/C++11FAQ.html#narrowing
That check on narrowing conversions, especially with POD types, is one of the reasons why I use uniform initialization. I've caught a lot of simple "what a dumb mistake" issues before they blew up and made things worse.
@newbieg
@Ganado
@doug4
@MikeyBoy
@jonnin
@keskiverto
@JLBorges
@Furry Guy

Thank you all for replying and helping. This code is for demonstration of objects, pointers, and scope purposes ONLY as you all probably already guessed.

Now I understood Cube *c = CreateUnitCube(); is nothing but a pointer to receive the return value from CreateUnitCube().

Last thing I would like to get more information about:

Line 7 & 8.

1
2
Cube *CreateUnitCube() {
  Cube * cube = new Cube;
]

What is the point of making function CreateUnitCube() and the cube object pointers? Again this entire code is for educational purposes but still any reasons behind it? I can't get an answer from books.




You are creating your object on the heap/free store. The only way to create an object that uses heap memory is using new.

The amount of stack memory is limited, the free store has much larger limits.

Normally when you create a local object in a function it is created on the stack. That object is then destroyed when the function returns. Even if you "return" the object. That leads to undefined behavior. That's bad.

Creating the object on the heap allows it to persist beyond the scope of the function, it is usable in other parts of your program.

And it doesn't have global scope.

Your questions seem to be more about pointers in general than object pointers specifically.

https://www.learncpp.com/cpp-tutorial/67-introduction-to-pointers/
Last edited on
You had:
1
2
3
4
5
Cube*  CreateUnitCube() {
  Cube cube;
  cube.setLength(15);
  return &cube;
}

and ask about:
1
2
3
4
5
Cube*  CreateUnitCube() {
  Cube*  huba = new Cube;
  huba->setLength(15);
  return huba;
}

In both cases a Cube object is created. One function creates it as automatic variable, in the scope of the function, and the other creates it into Free Store.

The latter function creates also a pointer huba as automatic variable in the scope of the function. The huba stores the address of the Cube object.

Both functions modify the Cube object (setLenght).

Your function returns the address of the automatic variable. That address is within stack. The object is destroyed at the end of the function call and stack is unrolled. Therefore the address returned by the function call is invalid.

The other function returns the value that huba has. The value is copied before the huba is destroyed. The value is the address of the Cube object that is in Free Store. That object is not destroyed at the end of the function scope.
What is the point of making function CreateUnitCube() and the cube object pointers? Again this entire code is for educational purposes but still any reasons behind it? I can't get an answer from books.

Honestly, most code avoids dynamic memory (new, delete calls) as much as possible, so the point may just be 'education'. It is avoided by using the c++ containers (vector, list, map, .. etc) which are well debugged and do the new/delete management for you in a safer way. The above reasons (saving stack memory, and making objects persist) are valid and correct but usually avoidable.
You are creating raw pointers using new/delete calls. I suggest you look into what smart pointers can offer, automatic memory management.

https://en.cppreference.com/w/cpp/memory

Topic archived. No new replies allowed.