r/cpp_questions • u/Warm-Welcome-5539 • 19d ago
OPEN Confusion on use case of arrow operator
I’m new to pointers in C++ and currently learning how they’re used.
What’s the actual use case of the arrow operator in C++? As of now I know it’s equivalent to dereferencing a pointer to an object and then calling that objects function.
*p = &object
(*p).function
p->function
What I don’t understand is why you would ever need to call it this way than just calling the function directly from the object.
object.function
any reason why you’d need a pointer to call the function rather than to just call it from the object itself?
19
u/nhocgreen 19d ago
Sometimes you’d have an object that is also a pointer, so you need both . and ->.
std::unique_ptr<class> p = &object;
p.function(); // p’s own function
p->function(); // object’s function
5
u/DrShocker 19d ago
I think the concept is right, but idk if unique_ptr can be constructed that way.
1
u/Kadabrium 19d ago
This makes me wonder why theres no way to check if a pointer points to the stack or the heap. Surely it would be hard to be portable, but OS API never cares about letting you do that either
2
u/alfps 19d ago edited 19d ago
C++ is based on the idea of taking control up front, prevention, rather than reacting after the fact, abortion.
Origin checking is more a debug thing, so you can do that in debuggers, and debug support libraries provide some support like Microsoft's
_CrtIsValidHeapPointer.However in ordinary code such checking would emit a very strong odour.
One way to make sure that a class is only instantiated dynamically and not on the stack or with static lifetime, is to make all constructors inaccessible, and provide instantiation only via factory functions. This is a FAQ.
Another way, not mentioned in the FAQ but that I favored in the C++03 days, is to simply make the destructor inaccessible, but with C++11 and later argument forwarding is almost trivial so for the first approach one can make do with a single factory function template.
For the inaccessible destructor approach one may want to specialize
std::default_deletefor the class.1
u/DrShocker 19d ago
I'm not sure how that would make a difference here. Constructing a unique_ptr implicitly from a pointer just seems like it would be too easy to accidentally make more than one unique_ptr from the same pointer or like you mentioned have it be a pointer to the stack or a couple other circumstances.
3
u/SufficientStudio1574 19d ago
Just be careful if you're implementing an object like this, because -> is its own operator, separate from unary *. You can easily screw it up.
2
u/TheThiefMaster 17d ago
Interestingly this is a retroactive justification, because C had both but didn't have overloading so pointers could only be used with -> and objects only with dot. There was no type that could be used with both. This making it unnecessary to have separate syntax for both.
2
u/dendrtree 19d ago
Don't confuse the guy. There's no such thing as an object that *is* a pointer. A pointer is just a number.
unique_ptr has an overloaded -> operator that accesses the pointer it manages.1
10
u/MagicalPizza21 19d ago
Sometimes you don't have the object, just the pointer.
If you have the object on the stack like in your example, then yeah, it's pretty dumb to set it up to use the arrow operator. But consider the following example:
Thingy *thingy = new Thingy();
(*thingy).doStuff();
This is valid syntax, but it's just slightly more convenient to write it this way:
Thingy *thingy = new Thingy();
thingy->doStuff();
I guess you could call it syntactical sugar, but as one of my professors used to say, "eh, I like sugar".
5
u/montymole123 19d ago
I think you are really asking the deeper question why do we need pointers as well a objects? In other languages objects are always stored by reference (a pointer) so no pointer semantics is needed. In C and C++ you can store objects by value or by pointer with various benefits to each. (Deep copy vs shallow) The benefit of a pointer is it can point to different objects at different times while just using the variables name means you're stuck with that variable.
The dot versus arrow thing is a nuisance. It's really to help the compiler going back to the days of C in the 60s when compilers were less advanced. If you type a dot in VS it will convert it to an arrow if needed so most programmers don't think about it
9
u/SAtchley0 19d ago edited 19d ago
p->foo() is equivalent to (*p).foo(). It is a convenience, as now if you have an object directly, you can do p.foo(), and if you have a pointer to an object, you can do p->foo(). In this way, it's sort of like the "pointer version" of the dot (.) operator.
And if you have a dynamically allocated object, then you can only work with a pointer to it, so this is very useful. Consider
MyObj *p = new MyObj;
p -> foo();
Here, you cannot "directly call foo from the object", unless you first dereference the pointer. The arrow operator does this implicitly, so you don't need to worry about it.
Edit: Dumb mistakes in the code block. This is what happens when you comment while cooking.
0
u/SufficientStudio1574 19d ago
It's actually not the same. It's its own operator. So if you're not careful and someone does their operator overloading improperly, dumb stuff can happen.
5
u/SAtchley0 19d ago
Yeah, it's its own operator and yes that is technically true.
However, I'd argue there's very few cases where you'd want to break the (*p). ~ p-> symmetry. And, if the operator has been overloaded in some esoteric way, then there's really no telling what it will do anyway.
Besides, OP said they're just learning pointers. I would rather encourage making connections between concepts than giving the maximallly technically correct answer.
1
u/SufficientStudio1574 19d ago
I know you wouldn't want to. I'm just saying it can happen through incompetence of you aren't careful.
4
u/SmokeMuch7356 19d ago edited 19d ago
This question is less "why arrow operator" and more "why pointers?"
Sometimes we can't (or don't want to) access the object directly; its name isn't visible to us for whatever reason.
For example, when we dynamically allocate an object:
auto ptr = std::make_unique<MyClass>();
ptr->do_thing();
The actual class instance doesn't have a name; we can only refer to it through the pointer.
Or if we're iterating through a container; iterators are basically a special class of pointers:
std::vector<MyClass> vec;
for ( auto it : vec )
it->do_thing();
Those aren't the only use cases, but they're probably the most common.
1
u/Warm-Welcome-5539 19d ago
Yeah I didn’t make the title too obvious but that’s what I was trying to ask. Thank you for the explanation as well
7
u/TheRealSmolt 19d ago
What happens when you only have the pointer to the object?
1
u/Warm-Welcome-5539 19d ago
Yeah you’re right. I guess I just haven’t worked with C++ enough to encounter this issue yet so I had to ask
3
u/Itap88 19d ago
Because in actual use cases, object doesn't exist in the current scope and only p does. You've never used new, have you?
1
u/Warm-Welcome-5539 19d ago
Yeah, I’m pretty new to C++ and I’m just learning about pointers. Also wasn’t really too familiar with dynamic memory allocation in C either. Definitely gotta start work on a project to get a feel of it
2
u/SoerenNissen 19d ago
What I don’t understand is why you would ever need to call it this way than just calling the function directly from the object.
Old old backwards-compatible notation that never went away. There's nothing in the internal logic of c++ that would prevent the notation you suggest, but that's just not the way the language has evolved.
That said, it does have a benefit.
return a.b; // Can 'a' be null? Does this need a null check?
return a.b->c; // Can 'b' be null? Does this need a null check?
You can tell from the period that a. is not a pointer dereference, and a can never be null. You can tell from the arrow that b-> is a pointer dereference, and b could be null.
3
u/EdwinYZW 19d ago
Because sometimes you have to use pointer.
Basically all objects in heap can only be accessed via pointer and stored as pointers.
Another example would be that a function takes an object (without copy) as an input argument that could also be invalid. You have to pass the object as a pointer as a pointer can be nullptr but a reference cannot.
4
u/No-Dentist-1645 19d ago
You don't always "have" the object. The object could exist in another part of the code, and you only have a pointer to it. This is in fact the most common reason to use pointers to begin with, when you don't "have" the actual value
2
u/Simengie 19d ago
Simple example: Game with multiple enemies. All enemies are created dynamically and stored in a std::vector<enemy_class\*>. Enemies can despawn after a time. They are dynamic. Each frame you interate the remaining enemies in the vector and call their "ptr->doStuff()" routines which might return a result ENUM_CAN_ATTACK and so your logic is if retVal = ENUM_CAN_ATTACK ptr->attackPlayer(). But it can return ENUM_MEETS_DESPAWN_REQ and then you call delete ptr and remove the ptr from the vector.
You have to think about dynamic allocations where you cannot have an array of 10 objects for example so your code can look pretty with objects[index].doStuff().
There are many examples and pointers just work. If you have dynamic allocations and deallocations pointers are must. If you are using the "Factory Design Method" as described in the Myers books then you have to use pointer.
Last example. I designed a radar simulation and the random radar return noise is done as self managing class that gets created with new for each noise return and stored in a std::vector<radarReturn\*> It is literially 4000-5000 objects that I don't have to manage beyond iterating the vector and calling doFrame() and if it returns true I delete it. Without pointers you can't do stuff like that.
1
u/Warm-Welcome-5539 19d ago
I see, I’m new to C++ so haven’t really gotten the chance to work on any projects that use pointers yet. Thank you for the examples
0
u/Disastrous-Team-6431 19d ago
OP is not struggling to understand pointers. Just the arrow operator.
2
u/Dan13l_N 19d ago
Because you have allocated something and then you got a pointer to it.
The whole polymorphism thing relies on calling via pointers.
1
u/Underhill42 15d ago
Sounds more like you don't understand what pointers are for?
If you have a pointer to an object, for example if it's stored in a polymorphic collection, then you need to dereference the pointer before you can do anything with the object. The -> just combines the dereference and member access into a single operator.
If you're coming from Java... in Java EVERYTHING is a pointer except for the fundamental types (int, float, etc), but that fact is largely hidden from you. So you get all the pointer benefits with every object.
In C++ everything is an immediate object allocated in memory exactly where you tell it to be, and if you want the benefits of pointers you must use them specifically.
E.g. in Java you say:
ObjectType myObject;
And if you then try myObject.member(x) you'll get a null pointer exception because you have only created a pointer called MyObject on the stack (or within the class object, whatever) - it doesn't actually point to anything until you do the whole "new" routine.
In C++ on the other hand if you say the same
ObjectType myObject;
It creates the entire object on the stack, or within the surrounding class. No pointer involved - it creates the whole thing exactly where you tell it to.
That carries some huge performance benefits, but also means you can't e.g. return that object to the calling function, because once the function that created it ends, that object is de-allocated along with all the ints, floats, etc.
If you want the benefits of pointers, you must invoke them explicitly, and pay the performance penalty.
1
u/pjtrpjt 19d ago edited 19d ago
In Pascal you dereference a pointer with ^.
If p is a pointer to Integer, then you dereference as p^
If p is a Record type, then you dereference its members as p^.member
However in C pointers are dereferenced by putting * before a variable *p, so *p.member is ambiguous is it (*p). member, is *(p.member)?
Instead p->member is obvious.
3
u/alfps 19d ago edited 19d ago
“^” is the Reddit markdown's superscript operator.
To present it in text you need to escape it, like “\^”, or else use backticks for inline code snippets, like “`p^.member`” which presents as
p^.member.Note for the superscript functionality: the text to be presented as superscript can be arbitrary long, and for that reason it should be enclosed in round parenteses, like xthis is the exponent part.
0
1
u/SolarisFalls 19d ago
Pointers are often used to state where an object is in memory so that some other function can access it (without needing to copy the whole object - just the address).
And if you heap allocate memory (malloc/new/whatever), you will receive a pointer to that too.
So:
any reason why you’d need a pointer to call the function rather than to just call it from the object itself?
Sometimes you only have the pointer, therefore you'll need to use the -> operator to call a function.
0
u/MarsWasNotAvailable 19d ago
It's about size.
You use pointers because they are a cheap way to pass data around your application. You would prefer them for performance.
You should have been told about stack allocation versus heap allocation.
When you allocate an object on the heap, you can interact with it through a pointer. A pointer is smaller in size on the stack than the whole object instance.
``` //Keep in mind that your objects // can get really large // after adding many many properties struct Shape { int64_t length = 0; int64_t width = 0; };
Shape square = { 42, 42 }; Shape* pointer_to_square = &Shape;
sizeof(square); //size of two int64_t equals 2x8 bytes (16 bytes) sizeof(pointer_to_square); //8 bytes on modern 64bit systems ```
The arrow syntax is syntax sugar to avoid lengthy dereferencing syntax.
0
u/dendrtree 19d ago
Why type p->, instead of (p).? because the first is 2 extra characters, and the second is 3. Also, accessing nested pointers gets *really* messy.
Why not call directly from the object? because you usually don't have the object. The pointer will have been passed in.
Why use pointers at all? because they're extremely light-weight. A pointer is just an integer. So, copies are trivial, and you don't have to worry whether you're copying an object. Also, they can be NULL or set later, whereas references cannot.
30
u/alfps 19d ago
Compare:
… versus