4

so i'm learning c++ and i just learned about dynamically allocated memory for class. there's something that make me it feel weird.

int main()
{
    person* pOne = new person("mike", 35);
    cout << pOne << " " << pOne->getName() << endl;

    person pTwo = { "dave", 30 };
    cout << pTwo.getName() << endl;

    return 0;
}

i think that when we want to call getName() function in pOne, we should do it like *pOne->getName() because pOne hold the memory location, not the person object itself. but if i do that i'll got compiler error.

i do it with pTwo that not dynamically allocated and it work like i tought.

so, can someone explain the logic of not using "*" when trying to call function?

drescherjm
  • 10,365
  • 5
  • 44
  • 64
FDuldul
  • 167
  • 1
  • 10
  • 1
    FDuldul • This is a very insightful question (and Angew & Ivan have good answers). The one extra bit of fun is a smart pointer like `std::unique_ptr` is an object, which has an `operator->`, which the `->` will be applied to every returned object until the returned type is a pointer, and then perform the pointer member access. That is how a lot of the "magic" of C++ happens around `->`. You don't get that magic with `(*a).b`. – Eljay Feb 21 '18 at 15:15
  • @Angew • hmmm, how do you chained `operator*`? I posted an example of chained `operator->` on question https://stackoverflow.com/questions/48953206 , is there special syntax to get similar magic with overloaded `*`? (Maybe I should post a new question.) – Eljay Feb 23 '18 at 19:15
  • @Eljay Sorry, I must have misread the comment. I didn't notice the mention of chaining. – Angew is no longer proud of SO Feb 23 '18 at 19:23
  • @Angew • ah, okay, nevermind. :-) I thought I might be missing some interesting (but not often used) dark corner feature of C++. – Eljay Feb 23 '18 at 19:29

4 Answers4

11

The built-in operator a->b is defined as (*a).b, so the dereference is "hidden" inside the -> operator.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
6

-> is just a syntactic sugar: a->foo is the same as (*a).foo. So when you use -> you in fact dereference the pointer.

Ivan Smirnov
  • 4,365
  • 19
  • 30
2

i do it with pTwo that not dynamically allocated and it work like i tought. so, can someone explain the logic of not using "*" when trying to call function?

You mixed different concepts, it is not related how you allocated memory, but on which type of variable you access member:

struct Foo { void bar(); };

Foo f; // type of f is Foo
f.bar(); // access on type Foo is through .
Foo *pf = &f; // type of pf is Foo*
pf->bar(); // derefence and access on type Foo* is through ->
(*pf).bar(); // the same as above and since type of *pf is Foo we can use .
(&f)->bar(); // type of &f is Foo*, so we can use ->
(*(&f)).bar(); // crazy stuff
Slava
  • 43,454
  • 1
  • 47
  • 90
1

It'll be clearer if you change the way you write your code.

Instead of:

person *pOne = new person("mike", 35);

Write:

person* pOne = new person("mike", 35);

This means the same to the compiler, but makes it clearer to humans that this is a person* (a pointer-to-person) called pOne.

Now it makes more sense that you still perform pointer operations on pOne, not on *pOne.

The older, right-aligned asterisks approach comes from the C world, in which people wanted you to think of it more like "there is a person at *pOne". And, in your case, that is true: (*pOne).getName() would also have worked. However, that looks pretty ugly to me, and *pOne isn't always an actual person — it depends on what (if anything) the pointer is set to.

The key point here is that -> (as in pOne->getName()) does that dereferencing for you. Writing (*pOne)->getName() would be doing it twice.

Your final example of pTwo.getName() is just a bog-standard object access.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • inb4 "you can't write your code like this because there is one edge case which you shouldn't be using anyway where it doesn't work" – Lightness Races in Orbit Feb 21 '18 at 15:36
  • or inb4 "the compiler sees it the first way therefore you must too (even though you are not a compiler)" – Lightness Races in Orbit Feb 21 '18 at 15:37
  • unfortunately `type* var` is against syntax rules so it can create another misunderstanding. – Slava Feb 21 '18 at 15:38
  • @Slava what syntax rules is it against? I prefer person* pOne because it fits more logically into how I think about pointers. Bottom line is that whichever one you choose just be consistent. – noobcoder Feb 21 '18 at 19:52
  • @noobcoder "what syntax rules is it against?" - `int* p1, p2;` is misleading, as syntax rules bind * to variable, not type. I think best would be to create a type alias but that is usually too verbose. – Slava Feb 21 '18 at 19:56
  • 1
    @Slava correct, but I think that rule is what Lightness Races was talking about. Just because that edge case exists doesn't mean you have to stop coding a certain way. I just avoid that edge case by never declaring more then 1 var on a line, which I don't find as readable anyway. – noobcoder Feb 21 '18 at 20:04
  • @noobcoder it certainly would not stop you from using it, and I do not try to. Problem is I do not know if it is good to recommend to use such practice for a novice programmer, as he/she would not know what additional restriction it implies and probably would not have enough self discipline to follow it properly. So as I said such rule unfortunately conflicts with syntax of the language and of course it is too late to change it. – Slava Feb 21 '18 at 20:09