Friendship and circular dependencies

Hello, I would like to know how I could solve the following circular dependency.

class_A.h
------------
#include "../include/class_B.h"
class class_A
{
public:
...
friend void class_B::funct();
...
};

class_B.h
------------
#include "../include/class_A.h"
class class_B
{
public:
...
void funct();
private:
class_A temp;
};

Thanks a lot in advanced!!
Last edited on
What makes you think that's what this is?
What?

@OP
You need to make one of the classes have a forward reference. Good luck!
Thanks a lot for your replys!

What makes me think this is a circular dependency problem is that I have to include "class_B.h" in class_A.h to refer to the member function class_B::funct(), and I have to include "class_A.h" in class_B.h to declare a class_A object.

I had already thought of forward declarations, but it doesn´t seem to work. If I insert it in file "class_A.h" then the compiler does not know the member function class_B::funct, and if I insert it in file "class_B.h" the compiler does not know the object class_A.

Maybe I am missing something... thanks!!
Last edited on
You are missing something. Google "forward class reference". Sorry I can't explain more right now... gotta go!
Nope :) ... I can't see how using forward declarations could solve this problem. Where should I use them??

Thanks a lot!!
Last edited on
In _A.h just add
class class_B; (declaration)
before class_A's definition.


I misread, sorry.
Last edited on
Thanks for your reply L B. What you say doesn't work, I get the following error (after removing the #include ...class_B.h)

C:\temp\dep_Test\src\..\include\class_A.h|11|error: invalid use of incomplete type 'struct class_B'| 9 (LINE: friend void class_B::funct(); )

C:\temp\dep_Test\src\..\include\class_A.h|5|error: forward declaration of 'struct class_B'| (line class class_B; )

Last edited on
Sorry, I misread the code and the topic. Does this help?
http://www.cplusplus.com/forum/unices/21527/
Thanks L B! this indeed solves the errors, but what I am interested in is to understand what the problem is with my code.

The problem appears when I declare the friend member function, and as far as I know, this should not interfere with anything and should be possible to do.

Am I wrong??
well, friends are an acceptable C++ construct, but for practical uses, you should use it as little as possible, as it breaks encapsulation

so whenever possible, refactor your code to avoid using it

also, circular dependencies are usually a flaw in the design; in fact, whenever possible, dependencies should always go in one direction (top to bottom) - however, when there is tight coupling that cannot be avoided between two classes, I will usually keep the two classes in the same source file for easier maintenance

specifically, what is the relationship between class A and B? post your real code - perhaps there is a way to loosen the coupling between the two.
as it breaks encapsulation

No it doesn't. http://www.parashift.com/c++-faq-lite/friends.html#faq-14.2
of course, it breaks encapsulation (don't believe everything you read - think about what they are saying)!

FAQ wrote:
You often need to split a class in half when the two halves will have different numbers of instances or different lifetimes. In these cases, the two halves usually need direct access to each other (the two halves used to be in the same class, so you haven't increased the amount of code that needs direct access to a data structure; you've simply reshuffled the code into two classes instead of one). The safest way to implement this is to make the two halves friends of each other.

damn! making A a friend of B is apparently not bad enough - let's also make B a friend of A?!?!?
you can maintain that code if you like - I prefer not to

the safest way in that case, is to refactor and place proper parts of the code into a nested class and control access properly - not break it wide open in both directions by making mutual friends

using a friend in the example described in that FAQ is definitely a sign of laziness, the evil kind

edit: I guarantee that if you do what the FAQ suggests, one day, A will grow and B will grow, and before you know it, you will quickly evolve your code into the OOP equivalent of spaghetti code (actually, it will be harder to debug than imperative spaghetti code)
Last edited on
kfmfe04 wrote:
of course, it breaks encapsulation


Of course it can break encapsulation, that doesn't mean it does break encapsulation.

One of the more practical applications for friendship is an iterator for a container class.

Clearly the container and the iterators are separate objects, so you can't use the same class.

Furthermore, the iterator will need a way to access the raw data that the container manages (in some form)

Lastly, you don't want the end user to be able to access the raw data the way the iterator needs to.


Therefore, to enhance encapsulation you give the iterators a private constructor, and make the container a friend. Now the container can create iterators as needed, but the end user is not exposed to how it works internally.

The only option around that is to build iterating functionality into the container class instead of using a separate iterator class (yuk), or make the key constructor public, exposing inner storage and management details to the user (poor encapsulation).

So in that case, friendship enhances encapsulation. It does not violate it.

AFAIK that's how containers like std::map and std::list work. I don't know about you, but I consider them to be very well encapsulated.



EDIT:

Though I agree with you on your main point. friendship is more often than not abused and used incorrectly. Such tight coupling is very rarely needed between classes.
Last edited on
Disch, you have listed a very specific case where using friend is warranted

but if I understand correctly, that's making the iterator a friend for the container so that you can only get an instance of an iterator through the container, which is fine

also, I agree that in making iterator a friend for the container, you are breaking class level encapsulation (by using friend) in order to provide for library-level encapsulation (users of the library cannot instantiate an iterator), which makes life harder for the maintainer, but easier for the user, which I'm okay with, especially since I don't maintain the STL

however, notice that FAQ actually advocates making each class a mutual friend of the other as the safest way to implement something?!?

maybe you can show me an example where that is true, because I can't seem to come up with anything

edit: never mind - don't waste your time - I just got annoyed by the FAQ dispensing advice that most readers wouldn't take the time to think about and evaluate on their own; there are many answers in that FAQ that seem to try to show how clever the writers are, rather than dispensing sound advice
Last edited on
kfmfe04 wrote:
you are breaking class level encapsulation (by using friend) in order to provide for library-level encapsulation (users of the library cannot instantiate an iterator)


Sounds to me like you're splitting hairs. Though I guess it depends on how you define your terms.

One could argue that std::list<T>::iterator is part of the std::list<T> class (which it is, depending on how you define "part of") and therefore it does not violate class level encapsulation. It's just two different parts of the same class talking to each other.

I won't make that argument though, because I don't care enough about it.



From my viewpoint, encapsulation is about separating interface from implementation. STL container classes, with their use of friendship, do exactly that. Whether you define that separation on a class level or on a broader level is completely arbitrary and irrelevant.

The bottom line (again, from my viewpoint) is that:

- with friendship, std::list<T> and std::list<T>::iterator can be properly encapsulated from the user's standpoint

- without friendship, they can't be

Going from that, friendship does not hinder encapsulation in any sense of the word.

So if you want to draw the line and say that "encapsulation means it must be contained within a single class", then you're either left with compromising that with friendship, or violating it completely to expose internals to the end user.


But again drawing that line at a class boundary is arbitrary. It's just the most convenient place to draw the line in most situations.

which makes life harder for the maintainer, but easier for the user,


That's kind of the point. In fact, you're more or less describing OOP as a whole with that statement.

Compare std::string (OOP) verses strcpy, etc functions (procedural).

- string is a lot harder to write and maintain
- string is a lot easier and safer to use


While maintaining string might be more difficult, maintaining programs that use string is easier.

When working on larger projects, maintaining individual classes might be more difficult, but maintaining the project as a whole is a lot easier.


however, notice that FAQ actually advocates making each class a mutual friend of the other as the safest way to implement something?!?


I'm less interested in defending the FAQ author than I am in defending the merits of friendship.

I don't really agree with his example, but I do agree with his overall message.
kfmfe04
Your terms are too narrow and you are putting your own personal bias on the meaning of encapsulation in C++.

Friends do not break encapsulation. A friend function|object is part of the class API. What breaks encapsulation is when unrelated, external things know too much about the inner workings of your class. Friends don't do that.**

A prime example is the overloaded stream insertion and extraction manipulators. Given some class, Foo, does it break encapsulation for a function to be defined as:
1
2
3
4
std::ostream& operator << ( std::ostream& outs, const Foo& foo )
  {
  ...
  }

Of course it does not. Serializing the object is part of the class API.

Just because the serialization is not a member function does not mean that it breaks encapsulation.

I don't much care for the FAQ's example either, but it is just one of many. Marshall Cline has, I suspect, far more experience in computer systems than you do, but beyond that, he is intimately familiar with the language's design, having served on the ISO C++ standardization committee. As a well-known and recognized, preeminent computer scientist who is very specific in his choice of words in technical issues, I think his opinion holds some hefty weight when he says, quite clearly, that
If they're used properly, they enhance encapsulation.

He also addresses your point of argument directly:
(Many people think of a friend function as something outside the class. Instead, try thinking of a friend function as part of the class's public interface. A friend function in the class declaration doesn't violate encapsulation any more than a public member function violates encapsulation: both have exactly the same authority with respect to accessing the class's non-public parts.)


**caveat: Friends could be used to break encapsulation, but simply that it is possible to abuse the language doesn't make a valid argument against it. You can abuse anything. The abuse of a thing is a different topic than the valid use of a thing. That is logic 101.

edit: never mind - don't waste your time - I just got annoyed by the FAQ dispensing advice that most readers wouldn't take the time to think about and evaluate on their own; there are many answers in that FAQ that seem to try to show how clever the writers are, rather than dispensing sound advice
And I presume that by "sound advice" you mean advice that comes from years of use in industry. I think that you are trying to show how clever you are, instead of simply accepting that you were wrong about encapsulation and friends and moving on with your newfound tidbit of knowledge.

Bye.
sorry - I'm too busy porting 30k LOC of C++ production server code into Google's Go to reply

...expect it to take <8k LOC in Go

I think I'm done with C++

bye
All I really have to say here is that friend does not break encapsulation, simply because private does not provide encapsulation, at least in my understanding of the term "encapsulation".
Topic archived. No new replies allowed.