22

In C++ we know that for a pointer of class we use (->) arrow operator to access the members of that class like here:

#include <iostream>
using namespace std;

class myclass{
    private:
        int a,b;
    public:
        void setdata(int i,int j){
            a=i;
            b=j;
        }
};

int main() {
    myclass *p;
    p = new myclass;
    p->setdata(5,6);
    return 0;
}

Then I create an array of myclass.

p=new myclass[10];

when I go to access myclass members through (->) arrow operator, I get the following error:

base operand of '->' has non-pointer type 'myclass'

but while I access class members through (.) operator, it works. These things make me confused. Why do I have to use the (.) operator for an array of class?

Salim Azak
  • 79
  • 10
lukai
  • 536
  • 1
  • 5
  • 20
  • 6
    Show, how did you get access to elements in arrays. Perhaps, you did something like this: `p[0]->setdata(5,6);`, which, of course, incorrect, since `p[0]` is no more a pointer. – awesoon Jul 18 '13 at 06:43
  • Please check this link It may answer your question http://stackoverflow.com/questions/8462895/how-to-dynamically-declare-an-array-of-objects-with-a-constructor-in-c – vinod Jul 18 '13 at 06:43
  • Try understanding what array really is and then move on to classes. – darxsys Jul 18 '13 at 06:47
  • what members are you trying to access? and how? they are all private. Please post the actual code that reproduces the problem – xaxxon Jul 18 '13 at 06:48
  • I added the following to your program and still got no errors (and don't expect any) p=new myclass[10]; p->setdata(1,2); – xaxxon Jul 18 '13 at 06:50
  • are we to assume you are saying p[2]->setdata? – xaxxon Jul 18 '13 at 06:52
  • @xaxxon, yes I am saying p[2]-> setdata – lukai Jul 18 '13 at 07:07
  • Also: use `std::array`, or `std::vector`. – rubenvb Jul 19 '13 at 08:21

6 Answers6

30

you should read about difference between pointers and reference that might help you understand your problem.

In short, the difference is:
when you declare myclass *p it's a pointer and you can access it's members with ->, because p points to memory location.

But as soon as you call p=new myclass[10]; p starts to point to array and when you call p[n] you get a reference, which members must be accessed using ..
But if you use p->member = smth that would be the same as if you called p[0].member = smth, because number in [] is an offset from p to where search for the next array member, for example (p + 5)->member = smth would be same as p[5].member = smth

Opsenas
  • 722
  • 5
  • 15
  • 1
    Some improvements: you can write `p[0]` in first case(`myclass *p`) and `p->setdata(42, 42)` in second(`p=new myclass[10];`) as well. Doesn't matter, what did you use, `new` or `new[]`. – awesoon Jul 18 '13 at 06:50
  • 2
    "(p + (sizeof(myclass) * 5).member = smth would be same as p[5].member = smth" - actually not, because the compiler automatically operates in units of the pointer-to object's size when adding an integer to a pointer, so simply "*(p + 5)" is already the same as "p[5]". – Tony Delroy Jul 18 '13 at 17:41
  • @Opsenas `(p+5).member`, is it correct? `*(p+5).member` or `(P+5)->member`, is it the correct way to access p[5] ?? – aaroh Jun 18 '18 at 11:31
  • @aaroh (*(p+5)).member and (p+5)->member are both legit if you defined p as pointer – Opsenas Jun 18 '18 at 15:51
  • @Opsenas But in your answer, you are using `(p+5).member`. Is it correct?? – aaroh Jun 20 '18 at 06:49
  • 2
    @aaroh tbh i've got lost, yes you're correct, fixed – Opsenas Jun 20 '18 at 12:12
10

Note that for a pointer variable x

myclass *x;
  • *x means "get the object that x points to"
  • x->setdata(1, 2) is the same as (*x).setdata(1, 2) and finally
  • x[n] means "get the n-th object in an array".

So for example x->setdata(1, 2) is the same as x[0].setdata(1, 2).

TobiMcNamobi
  • 4,687
  • 3
  • 33
  • 52
6

Because by using [] like p[3] you are already dereferencing the pointer to array + index shift. After that you have to use ".", since p[3] is an object, not a pointer.

xaxxon
  • 19,189
  • 5
  • 50
  • 80
SGrebenkin
  • 572
  • 4
  • 10
  • No , he means the following: "p=new myclass[10]; but then , when i go to access myclass members through (->) arrow operator". He tries to do the trick p[N]->setdata(5,6), 0 <= N < 10. But it's not correct. – SGrebenkin Jul 18 '13 at 06:54
4

After...

MyClass* p = new myclass[10];

...p is a pointer to an array of MyClass objects. The "pointer to an array" thing has to be delt with first. Whenever you have a pointer to an array, p[n] effectively gives you a reference to the nth element in the array, so you effectively have a MyClass&. That's why . is then needed to access MyClass members ala p[n].member, and why the pointer-specific -> notation is erroneous in this case....

Note that p->member (for any member) is still valid and equivalent to p[0].member, so you can only use it for accessing the first element in the array. I strongly recommend you don't use it at all whenever you're in a programmatic context where 'p' is known to be pointer to the array, as it hides the fact that p is an array. Sometimes though you may create another pointer - say q - with the purpose of refering to a single array element - may or may not be [0] - and in those situation's it's fair to use q->member. Variables like q may be used for iteration over the array too. But, sometime you're going to need to delete[] p; - so you won't tend to change p beforehands... you don't want to lose track of the p[0] address or delete[] p; will be Undefined Behaviour (which is allowed to be implementation defined and happens to be on Windows if p still points within the array, but won't be portable).

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
3
myclass *ptr;

ptr = new myclass();     // ptr points to a single object
ptr->doSomething();      // calls doSomething on the object _pointed to_

ptr = new myclass[10];   // ptr points to multiple objects
ptr->doSomething();      // calls doSomething on the first object _pointed to_
(ptr+1)->doSomething();  // calls doSomething on the second object _pointed to_

auto val = ptr[2];       // fetches a reference to the second _object_ to val.
val.doSomething();       // calls doSomething on the _object reference_ val.

In other words, when indexing the array to fetch the n'th element, you're not fetching a pointer to the n'th element, you're fetching a reference to the actual object, and the members of that need to be accessed using . syntax.

Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294
  • @xaxxon The problem would (as far as I can see) stem from him trying `p[x]->doSomething()`, when the operator [] actually _returns a reference_ that needs to be called with `.` – Joachim Isaksson Jul 18 '13 at 06:53
  • yeah, that's why I'm a bit miffed he didn't actually post code to reproduce it. – xaxxon Jul 18 '13 at 06:54
  • @xaxxon: He showed the working code, then said "Then I create a array of "myclass"" and shows how (`p=new myclass[10];`) but doesn't list the code that he must have had to produce the error - something like `p[2]->doSomething();`. – Tony Delroy Jul 18 '13 at 07:02
  • Yeah, but he didn't say that, and p->doSomething still works fine. – xaxxon Jul 18 '13 at 07:02
  • @xaxxon: it does for a certain definition of "works", but if he had to ask the question then he probably didn't know what it was doing anyway (i.e. affecting element `[0]`). – Tony Delroy Jul 18 '13 at 07:04
1

Perhaps its insightful to consider that, given

myclass obj;
auto p = &obj;  // avoid `new` with plain pointers. That combination is
               //  safer replaced by unique_ptr or std::vector.

the following will all work and are equivalent:

p->setdata(5, 6);
(*p).setdata(5, 6);
p[0].setdata(5, 6);
0[p].setdata(5, 6);

Showing that [] is really a pointer-dereferencing operator, just with the extra functionality that you can add offsets into a plain C-array.

It's generally questionable to use C-arrays in C++ code; the obvious alternative to your example is std::vector:

std::vector<myclass> array(10);

Here, array[n] can be used much like previously p[n], but

  • You don't get any stupid pointer-ideas, because there are no pointers in the interface
  • You get proper automatic memory management, i.e. when the array goes out of scope it automatically deletes the objects and its memory
  • You can get bounds-checks if you want (array.at(n))
  • You can easily loop over the whole array, with (C++11) for(auto& obj: array){...}.
leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • Since you've mentioned C++11, you may also want to be aware of [`std::array`](http://en.cppreference.com/w/cpp/container/array) – Bill Lynch Jul 18 '13 at 07:54
  • Good point, though I don't think it really belongs in this question. (We're talking objects/arrays on the heap here. Automatic C-style arrays and `std::array` are on the stack – which is oftentimes advantageous!) – leftaroundabout Jul 18 '13 at 07:57