Accessing an inherited class's variables

Member
Posts: 20
Joined: 2007.02
Post: #1
The title's probably a little misleading, but I didn't know what else to call it.
(Using C++ in xCode)

In my object management system, I have the parent class 'cParentClass', and object classes e.g. 'cEnemy'. This means I can store them all in a nice big array, and not have to worry about executing cycle events (performed every frame) for each individual object class. My problem comes when, in class 'cEnemy', I try to access the x variable in cPlayer, because of the error:
"error: 'class cParentClass' has no member named 'x'"
In the line:
"targetX = omsList[iPlayer]->x;"
Where 'omsList[iPlayer]' is the instance of cParentClass.
Makes sense, it doesn't have the member, but if I give it the variable, it's there in all my other classes as well, taking up memory isn't it? So is there a better way, or am I trying to access it in the wrong way?

Tobs
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #2
So are the parent class's private variables actually declared private? If so, there's your problem: private variables mean that absolutely nothing but that class can access those variables, including subclasses. Protected variables, however, are accessible by that class and subclasses, but not from other dependancy trees or globally.
Quote this message in a reply
Member
Posts: 20
Joined: 2007.02
Post: #3
Everything in all my classes is public, so any subclass should be able to get a variable from any other subclass shouldn't it?

I think the compiler thinks that I want to access the variables of the parent class, not the child, which is fair enough, because omsList is of type 'cParentClass' (with children stored in it), but I can get to the cycle function (defined as a virtual void in the parent class), so is there a way to get to the child's variables?

Tobs
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #4
Tobs_ Wrote:Everything in all my classes is public, so any subclass should be able to get a variable from any other subclass shouldn't it?

I think the compiler thinks that I want to access the variables of the parent class, not the child, which is fair enough, because omsList is of type 'cParentClass' (with children stored in it), but I can get to the cycle function (defined as a virtual void in the parent class), so is there a way to get to the child's variables?

Tobs
I see, you're trying to access a child class's variables from the parent class. In that case, your only option is to typecast to the child class. Even for virtual functions, they must be declared in the parent class if the object is of the parent class's type. (even if it's only polymorphically in that type) If you want to have some extra security making sure you don't typecast to the wrong type, you can use dynamic_cast<>(), which returns NULL if you cast to an unsupported type. (such as from a different inheritance tree) You can also have an integer with an enumerated value to store information about the type so you know exactly what type it is.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #5
Code:
((cSubClass *)array[i])->subclassThing

If you have to do something like this, you probably have very poor OO design.
Quote this message in a reply
Member
Posts: 254
Joined: 2005.10
Post: #6
I think what he is trying to do is declare a bunch of classes with a member function like update() but where each subclass uses its own implementation that accesses member variables only declared in the subclass. Like so:
Quote:class Parent:
virtual void update();
end Parent;

class Child:
member x;
void update: x++; end update;
end Child;

Parent instances[] = [ childA, childB, etc ];
for instance in instances:
instance->update();
end for;

It's been awhile since I did a major project in c++ and I don't remember how you make sure that the member function called is from the subclass and not the superclass.

Edit:
What you want to do is declare the member function as virtual in the parent.
Quote:virtual void Parent::update()
{
// Do stuff
}
Then you define update for your subclass.
Quote:virtual void Child::update()
{
// Do stuff with child members.
}
To use the Child's implementation you do this.
Quote:Parent* instance = new Child();
instance->update(); // Use the child implementation.
Finally if you need to force the use of the Parent implementation you do this.
Quote:instance->Parent::update(); // Use the parent implementation (I may be wrong on the syntax here...)
Quote this message in a reply
Member
Posts: 20
Joined: 2007.02
Post: #7
Ahh thanks, that's cleared some things up Smile

What about if I wanted to access a subclasses' variables though? Would I have to make a function to return the variable?

Tobs
Quote this message in a reply
Member
Posts: 254
Joined: 2005.10
Post: #8
Tobs_ Wrote:Ahh thanks, that's cleared some things up Smile

What about if I wanted to access a subclasses' variables though? Would I have to make a function to return the variable?

Tobs

Ok, well let me give you an actually compilable example. Wink

Code:
#include <iostream>
using namespace std;

class Parent
{
public:
    Parent() { p = 1; }
    virtual void foo() { cout << "Parent " << p << endl; }
protected:
    int p;
};

class Child: public Parent
{
public:
    Child() { p = c = 2; }
    void foo() { cout << "Child " << c << endl; }
protected:
    int c;
};

int main (int argc, char * const argv[])
{
    Parent* p1 = new Parent();
    Parent* p2 = new Child();
    p1->foo(); // Output: Parent 1
    p2->foo(); // Output: Child 2
    p2->Parent::foo(); // Output: Parent 2
    return 0;
}
Quote this message in a reply
Member
Posts: 20
Joined: 2007.02
Post: #9
That makes things a bit clearer, thanks Smile

2 Final Questions:
Can I access a subclasses' variables? I tried doing
cout << p2->Child::c;
but it didn't like that. Is it possible? Or do I use a function to return the variable?
Do I have do delete all the variables in the class myself in the destructor?

Tobs
Quote this message in a reply
Member
Posts: 254
Joined: 2005.10
Post: #10
When you declare a variable of type Parent, you are telling c++ that that variable responds to the 'interface' for the Parent. That means that using that variable you can only access members that are public to Parent, not to any Child classes.

If you know what kind of Child class you have, you can use typecasting to access the Child's variables.

Code:
Parent* p2 = new Child();
(Child*)p2->c;

Note that you must know what subclass you are using... so if ChildA has a variable a and ChildB has a variable b and you try to cast the ChildB to a ChildA you get very bad things happening... your program will probably crash when you try to access a or b from the wrong object.


When you create a variable with new, you create new storage on the 'heap'. When you delete a variable, you free the memory in the heap. Only delete variables you created with the new keyword.

When objects are deleted, their destructor is called. The destructor should be used to delete and remaining member variables that you haven't already deleted.
Quote this message in a reply
Member
Posts: 20
Joined: 2007.02
Post: #11
Hmm, that didn't work even after OneSadCookie had changed the code. I tried using:
Code:
#include <iostream>
using namespace std;

class Parent
{
public:
    Parent() { p = 1; }
protected:
    int p;
};

class Child: public Parent
{
public:
    Child() { p = c = 2; }
    int c;
};

int main (int argc, char * const argv[])
{
    Parent* p2 = new Child();
    cout << (Child*)p2->c;
    return 0;
}
But it just tells me
"error: 'class Parent' has no member named 'c'"
Which makes sense because it doesn't, but shouldn't it try and get the Child's 'c' variable instead?

Tobs
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #12
Check the order of operations. It does the -> first, which means it's trying to access c from p2 before the typecast. You will need to enclose the cast of p2 in parenthesis like so:
((Child *)p2)->c
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #13
Whoops, didn't notice that the parentheses were wrong too...

Bottom line is, as I said earlier, you should not be doing this. It violates every principle of OO design. If you think you need to, you should design your class hierarchy in another way.
Quote this message in a reply
Member
Posts: 53
Joined: 2006.10
Post: #14
Hi!
I worked on your program a little and took out the errors ^_^

Code:
#include <iostream>
using namespace std;

class Parent
{
public:
    Parent() { p = 1; }
    void SetP(int x) { p = x; }
    int GetP() const { return p; }
protected:
    int p;
};

class Child: public Parent
{
public:
    Child() : c(2) { SetP(c); }
    int c;
};

int main (int argc, char * argv[])
{
    Child* p2 = new Child();
    
    // show what they were
    cout << p2->c << endl;
    cout << p2->GetP() << endl;

    // then they change
    p2->c = 5;
    p2->SetP(15);
    
    // and now they are
    cout << p2->c << endl;
    cout << p2->GetP() << endl;
    
    delete p2;
    return 0;
}
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #15
You missed the whole point. The point was to have the Child exist as a Parent, but then access the fields of the child when it's existing as a parent. (hence the typecasting)
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  SQLite3 question: accessing by column instead of index? Toontingy 3 4,046 Apr 8, 2010 06:19 PM
Last Post: OneSadCookie
  C: Global Variables versus Parameters Lizard Man 10 5,872 Jan 13, 2010 08:22 PM
Last Post: Lizard Man
  Noob: Accessing Structures from Cocoa Classes MikeC 15 7,020 Oct 19, 2007 02:42 PM
Last Post: MikeC
  Problems with variables in Obj-C vnvrymdreglage 16 6,511 Oct 2, 2006 10:19 PM
Last Post: vnvrymdreglage
  Should global variables be pointers or full objects? ia3n_g 1 2,317 Aug 4, 2006 05:53 PM
Last Post: OneSadCookie