3

I know the difference between the points-to (->) and dot (.) operator but I don't see why the need for the two arrises? Isn't it always just as easy not to use pointers and just use the dot operator? From http://www.programcreek.com/2011/01/an-example-of-c-dot-and-arrow-usage/

    #include <iostream>

    using namespace std;

    class Car
    {
    public:
int number;

void Create() 
{
    cout << "Car created, number is:  " << number << "\n" ;
}
    };

    int main() {
Car x;
// declares x to be a Car object value,
// initialized using the default constructor
// this very different with Java syntax Car x = new Car();
x.number = 123;
x.Create();

Car *y; // declare y as a pointer which points to a Car object
y = &x; // assign x's address to the pointer y
(*y).Create(); // *y is object
y->Create();

y->number = 456; // this is equal to (*y).number = 456;
y->Create();
    }

Why ever bother using pointers? Just create Y as X was, it would work the same. If you say you need pointers for dynamically alocated memory, then why bother having the dot operator?

Eric
  • 1,356
  • 14
  • 24
Celeritas
  • 14,489
  • 36
  • 113
  • 194

7 Answers7

6

I think you're mixing two separate concerns.

First, the -> operator is unnecessary, yes. x->y is equivalent to (*x).y, but the -> operator is easier to type, so it's basically just a convenience.

The second part is whether to use pointers at all. And you're right, often you shouldn't. By default, just create your objects then and there, and refer to them direclty:

Foo bar;
bar.baz():

but pointers are still necessary for a lot of cases. Objects need to be able to reference other objects. A reference can do that, but it can't be reseated. Once it is initialized, it will always point to the same object.

A pointer can be updated to point to a different object.

Linked lists, tree data structures and countless other things depend on objects being able to point to other objects.

So yes, we need pointers. But we don't need the -> operator. We just use it because it's convenient.

jalf
  • 243,077
  • 51
  • 345
  • 550
  • `x->y` and `(*x).y` invoke two different, not necessarily related operators. If `x` is of pointer type, then the two are the same, but that's not required in general. – Kerrek SB Apr 08 '12 at 11:12
  • @KerrekSB: sure, but my point is that they're not both *necessary* In the absence of overloading, they're equivalent, and when overloading, you generally try to keep that equivalence. And if you don't, then the `->` operator could be *anything*. Then it could be easily removed, and an arbitrary new operator introduced instead. The "arrow as accessing member of the pointed-to object" operator is unnecessary because we can do the same by dereferencing the pointer. But sure, once the operator was there, it could be hijacked for other purposes through overloading – jalf Apr 08 '12 at 11:15
2

a. it just makes it easier to semantically understand the code without looking at the types, or having special notations like m_pszMyName. You can instantly tell reading the code what is a pointer and what is a value.

b. Think of the case of shared_ptr and overriding operators. shared_ptr<T>->get() means something else than shared_ptr<T>.get(). The first being the function in a pointed object, the second one being the function of the shared_ptr class itself. This is just one example, but you see the point.

Not_a_Golfer
  • 47,012
  • 14
  • 126
  • 92
1

From your link:

The following example should be a good one.

It is actually a bit confusing. Why would you ever create an object on the stack (Car x;) and then create a pointer to it to access it using ->?

Instead of trying to answer the implied question "Why do we need pointers?" I'll try to clear up any confusion that might have arisen from that example.

In your comment you say:

I'm wondering if there's a difference between objects that are created differently.

In the example there is only one object, the Car on the stack created by Car x; (to be complete there is also a Car-pointer on the stack, created by Car *y;). They go out of scope when main() exits, so their memory gets cleaned up.

But there is another way to create objects, which I guess you already know about based on your comment, and this is to use new to initialize them on the heap: Car *z = new Car;. Objects on the heap will never go out of scope, so you can keep using them after the function that called new exited, but you have to explicitly clean them up using delete to prevent memory leaks.

So there it is, a more realistic use of a pointer to an object: the return value of new.

Eric
  • 1,356
  • 14
  • 24
0

Isn't it always just as easy not to use pointers and just use the dot operator?

C/C++ like other higher order languages do not encapsulate the pointers with some sugar coating syntax. Pointer's arise naturally and the list below is not exhaustive

  1. Allocating memory from heap. Static data allocation, or allocating storage in stack is always not feasible. There are overheads with transfer of ownership, space constraint and dynamic nature of your program.
  2. Reading and writing files.
  3. Iterating over objects including C-Type string. You can use array access syntax, but there is little safety difference and arrays are degenerated to pointers when you pass to a function(size information is lost).

All the above can be encapsulated into objects when you thing from C++ perspective.

  1. FILE IO through iotream
  2. pointers through smart pointers(some from the C++98 and some in C++11 or eve boost)
  3. Iterators for STL Type Objects.
  4. Using reference

Nevertheless, Pointers are there even in languages where you don't see them explicitly. They are just encapsulated to higher order objects.

This explains to some extent why we can't think beyond pointers, the next part probably you are interested is in the syntax. Why do we atall need ptr->somemember instead of (*ptr).somemember. Its just shorthand for a repetitive usage. C/C++ programmers have got used to it and I have not seen till to date a single program using the superfluous syntax.

Abhijit
  • 62,056
  • 18
  • 131
  • 204
0

-> is just for short. Consider a class represent nodes of trees:

struct node {
    int data;
    node* left;
    node* right;
};

The member left is a pointer point to the left child of the node. Suppose we have a pointer to some node p, now we want to get the pointer point to the right child of the left child of the left child of p, using dot we have to write (*(*(*p).left).left).right, difficult to read and error-prone, using -> we can simply write p->left->left->right, very clear.

Cosyn
  • 4,404
  • 1
  • 33
  • 26
0

The existence of both -> and . operators in C++ is a direct influence from C. C makes the distinction between accessing an object through a pointer and accessing an object which is declared in the current scope.

In C++ references are a natural extension of accessing locally scoped objects.

I don't know whether the creators of C considered this, but I always used to use it as a small optimisation guide. Looking at a piece of code, you could see that -> would calculate the final address at runtime, whereas the . operator would calculate the address at compile time. This even works when accessing members of structures. Consider the following: myptr->mMember.mValue The offset from mMember to mValue can be calculated at compile time, whereas the final address calculation from the pointer must be calculated at run time. A minor consideration as far as optimisation is concerned these days, I'll admit, and with references in C++ it's no longer possible to do this, but 20 years ago it was something to bear in mind.

0

Yes, you could always use (*x).member instead of x->member, but would you really want to when x is a complex expression?

Having related things (* and . in this case) far away makes the source code less readable, so having the -> "in one place" is simply a nicer syntax.


As for the need for the concept of pointers, there are 2 main reasons:

1. Object Lifetime

There are two ways to allocate an object

  • On the stack.
  • In the dynamic memory.

The stack winds and unwinds as the flow of execution enters and exits functions, so the lifetime of a stack object is inevitably married to how long we stay in the function that created it.

If you need an object that lives longer than the function that created it, you need to create it in the dynamic memory, and the only way to identify such object is by its memory address, aka. pointer.

2. Object Sharing

Is there more than one other object that needs to access the object? If yes, then there is no way for these other objects to reference the shared object, other than holding its address.

Even if you have only one other object, but their lifetimes differ, the "lifetime" reason applies. If there is only one other object and their lifetimes match, than make it a field.

Branko Dimitrijevic
  • 50,809
  • 10
  • 93
  • 167