accessing functions from other objects

Pages: 12
Hi -

I'm trying to remember how to do this.

Let's say I have classes A, B, C and D.
Class A contains an object of class B.
Class B contains objects of classes C and D.
The object of class D wants to call a routine in the object of class C.

How do I make the routine visible? Do all the objects have to be in the public space of the including objects, or is there a preferred way to do this?

Thanks.
Well if you want a bit of refresh on classes you can go to: http://www.cplusplus.com/doc/tutorial/classes/

read this: (from the page)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// classes example
#include <iostream>
using namespace std;

class CRectangle {
    int x, y;
  public:
    void set_values (int,int);
    int area () {return (x*y);}
};

void CRectangle::set_values (int a, int b) {
  x = a;
  y = b;
}

int main () {
  CRectangle rect;
  rect.set_values (3,4);
  cout << "area: " << rect.area();
  return 0;
}



The scope operator (::) specifies the class to which the member being declared belongs, granting exactly the same scope properties as if this function definition was directly included within the class definition. For example, in the function set_values() of the previous code, we have been able to use the variables x and y, which are private members of class CRectangle, which means they are only accessible from other members of their class.
1
2
3
4
5
6
7
8
//forward declare all the things you know you need
class A;
class B;
class C;
class D;
void C::routine();

//now define everything 
Here's a shortened version of what I'm trying to do:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class A;
class B;
class C;
class D;
int	C::getC();

class A
{
	B b;
};
class B
{
	C c;
	D d;
public:
};
class C
{
	int c;
public:
	int	getC() {return c;}
};
class D
{
public:
	int	getD() {return c.getC();}
};

int main()
{
}


I get several compiler errors, including "invalid use of incomplete type 'struct C'"

What am I doing wrong?

Thanks.
Ah. You can only use Pointers and References to (only) forward declared classes/structures.
If you want to use B in C, or A in B you need to define the classes backwards.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

//C must be defined before class B
class C {
	int c;
 public:
	//here getC only needs to be declared for it's use in class D (if you wanted)
	// int getC() { return c; } //this would work
	int getC(); //but so should this (defined below main)
};

//D must be defined before class B
class D {
 public:
	int getD() { return c.getC(); }
};

//B must be defined beofre class A
class B {
	C c;
	D d;
};

class A {
	B b;
};


int main() {
	//...
	return 0;
}

int C::getC() { return c; }
I tried compiling your code above, and I get this:

'c' was not declared in this scope


On your line 14.

If I do the forward declarations:

1
2
3
4
5
class A;
class B;
class C;
class D;
int	C::getC();


I get this:

invalid use of incomplete type 'struct C'


on the line where the routine is forward-declared.
You need an instance of class C available to class D in order to call getC. This will mean class D will need to be defined after C for your code to compile (unless the instance is a reference or pointer.)

As for the second error I'm not entirely sure, but it doesn't really matter because forward declaring will not work here as you are not using references or pointers.

1
2
3
4
5
6
//line 11
class D {
	C c; //try adding this line
 public:
	int getD() { return c.getC(); }
};

Last edited on
OK...if that's the case, then I think I should use a different approach.

The application is a simulator of a modem. The modem object contains all the objects for the filters, amps, etc. Occasionally, these "sub-objects" need to communicate with each other, but in most cases, won't contain instances of each other.

Any suggestions on how I should design this?
There are two ways you can do this that I see. The first is a bit weird, and that's to pass the objects around as parameters where needed.
Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class B;

class A {
 public:
  void workWithB(B &b) { ... }
};

class B {
 public:
  void workWithA(A &a) { .. }
};

class Moden {
  //examle Components
  A a;
  B b;
};


The other, a bit more involved way, is to store a pointer to the modem each component is attached to (i.e. The filter can see the modem.) And possibly make the components friends of the modem so then can see it's other private/protected members. Basically all the Components have a way to see the router (via a pointer) and can then access its other components.
Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Modem;
class A;
class B;

class Component {
  friend Modem; //so class Modem can change the field modem

 protected;
  Modem *modem;
};

class A : public Component {
 public:
  //defined below
  void getA();
  void getB();
};

class B : public Component {
};

class Modem {
  //so each Component can access the other components
  friend class A;
  friend class B;

  A a;
  B b;

  //we use a pointer to suport polymorphic behavior
  void attachComponent(Component *c) { c->modem = this; }
};

//these methods (getA and getB) must be defined after class Modem
A A::getA() { return modem->a; } //That's me!
B A::getB() { return modem->b; } //some other component
//note: these will crash you program (and maybe your computer) if modem is NULL, so a check might be in order
// if(modem) return modem->a;
// else throw /*insert some null pointer error here*/; 


(I hope I didn't make any mistakes.) Let me know if it doesn't compile right, or if you have any questions.
Last edited on
Thanks for the detailed response. In looking at the options, I'm left with the feeling that somehow, poor design on my part has necessitated an overly complex solution to this.

I have to believe this kind of issue comes up all the time; if it doesn't, what techniques do people use to avoid it?

Thanks...
Actually that second example (using pointers) is exactly how I would approach this type of problem: one parent device and attached components that can see each other. However I'm sure there are other ways to do this.
I'm still trying to understand this technique. If I defined the modem class, declaring the components as protected, would they then have access to each other? That would make things a bit simpler.

Also, I notice you're still returning objects in some functions; would it be preferable to return pointers instead?
mzimmers wrote:
If I defined the modem class, declaring the components as protected, would they then have access to each other?

No. The only difference between private and protected is the protected member can be accessed by derived classes. Example:
1
2
3
4
5
6
7
8
9
10
11
class Base {
 private:
   int a;
 protected:
   int b;
};

class Sub : Base {
   // int getA() { return a; /*error*/ }
   int getB() { return b; /*this is fine*/ }
};

The reason class A can see the private members of class Modem is because Modem declared A as a friend.
1
2
3
4
class Modem {
   friend class A; //now members of A can see any member of Modem
   ...
};


mzimmers wrote:
Also, I notice you're still returning objects in some functions; would it be preferable to return pointers instead?

It depends. If it's possible to return NULL (meaning "no component") then returning a pointer may be better, however if you're always going to return a Component then a reference or copy would be fine.
1
2
A getA(); //return a copy
B& getB(); //return a reference 
mzimmers wrote:
I'm still trying to understand this technique.

Think about your modem as an object:
1
2
class Modem {
};

You modem has many attached components:
1
2
3
4
5
6
7
8
9
10
11
12
//a Component is an object
class Component {
};
//a Filter is a Component
class Filter : public Component {
};

class Modem {
   //you moden contains a Filter
   Filter filter;
   ...
};

Filters can see the modem's other components.
There are a few ways to do this. One would be to make the filter and other components public so anyone who uses your modem can edit them. Another would be to make public accessor methods
Filter Modem::getFilter() const { return filter; } so that anyone who uses your modem can use a copy of them, but not actually edit the components themselves. And another is like what I did, you can make your modem declare all the components friends of it so that they can access the other components, but outside of those classes the components are still private.
Can you tell me what you mean by "attached?"

I'm also trying to understand the place that the Component class has in the overall structure of this. I was hoping to do something like this (this code doesn't compile):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Modem;

class A
{
	Modem* pModem;
	int	m_i;
public:
	A(Modem* pM) : pModem(pM) {}
	int getA() {return m_i;}
	void setA(int i) {m_i = i;}
	void cycle () { int j = pModem->getB(); }
};

class B
{
	Modem* pModem;
	int m_i;
public:
	int getB() {return m_i;}
};

class Modem {
	A a;
	B b;
public:
	Modem() : a(this) {}
	int getA() { return a.getA(); }
	int getB() { return b.getB(); }
};


But, I have sort of a circular problem here: each class needs a full definition of the class it's trying to reference. Is this the problem you're solving by using the Component class?

Thanks.
And possibly make the components friends of the modem so then can see it's other private/protected members.
Too much coupling.
1
2
class Component {
  friend Modem; //so class Modem can change the field modem 
Same here. You will be better just giving a public function (perhaps using the constructor).

Try to follow the law of Demeter:
A method M of an object O may only invoke the methods of the following kinds of objects:
1. O itself
2. M's parameters
3. any objects created/instantiated within M
4. O's direct component objects
5. a global variable, accessible by O, in the scope of M

So in order to reach the other object mzimmers is using the modem as an intermediary.

About the error that you are getting. You will need to separate the declaration of the definition
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Modem;
class A
	{
		Modem* pModem;
		int	m_i;
	public:
		A(Modem* pM) : pModem(pM) {}
		int getA();
		void setA(int i);
		void cycle ();
	};

class B
	{
		Modem* pModem;
		int m_i;
	public:
		int getB();
	};

class Modem {
	A a;
	B b;
public:
	Modem() : a(this) {}
	int getA();
	int getB();
};

void A::setA(int i){
//...
}
//etc 


For further information http://www.cplusplus.com/forum/articles/10627/ (especially points 4,6, 7)
By extending a base class like Component, you can avoid retyping Modem* for each component. Also, you can use polymorphic methods for anything that takes a Component as a parameter, allowing you to avoid much more rewriting.
So, Mathhead, if I understand your technique, you're using "friend" instead of the back-pointer to give lower-level classes access to the base class, and thereby, to each other?
ne555: Thank you for the instruction on that; that helped a lot. I'm having a bit of difficulty applying it to my real-life problem, though. Here's the situation:

1. I have a class Soc, whose objects will contain many other classes.
2. One such class is ModCicComb.
3. The Soc class needs to know about ModCicComb's public functions, and vice versa.

So, my ModCicComb.h file contains:
1
2
3
4
5
6
7
8
9
10

#include "soc.h"

class	ModCicComb
{
	Soc* pSoc;
.
.
.
}


And the Soc.h file contains:

1
2
3
4
5
6
7
8
#include "ModCicComb.h"
Class Soc
{
	ModCicComb	mcc;
.
.
.
}


(all header files include guards)

The compiler objects because when soc.h includes modciccomb.h before defining the Soc class. I can think of a couple ways to solve this, but they're a little messy. Do you have a suggestion? Whatever the solution is, will be replicated in about 20 files (so mathhead's use of a parent class looks a little better now!).

Thanks...I hope I made this problem description clear.
... you're using "friend" instead of the back-pointer ...

No. All friend does is declare a particular class (or function) a friend of the class where the keyword appears. Example:
1
2
3
4
5
6
7
8
class Friend {
   ...
};

class Class {
   friend class Friend; //now Friend is a friend of Class
   ...
};

This allows the friend (class of function) to access any member in the class where the keyword appears. (Above, the class Friend can access any private or protected member of the class Class)

All this would mean is that you can declare your components as private which means they can't be directly accessed outside of the class, except by the classes friends which could be the other components.
Pages: 12