Including related files

Discussion in 'C++' started by Connell Gauld, Jun 2, 2005.

  1. Hi,
    I have what feels like a really stupid question and I'm sorry if it is
    asked a lot.
    Imagine I have two classes cShip and cPassenger. They each have a
    definition in their own header cShip.h and cPassenger.h and their
    implementation in cShip.cpp and cPassenger.cpp. Now here is the header
    file for each:

    #ifndef CSHIP_H
    #define CSHIP_H

    class cShip
    {
    ...
    cPassenger * owner;
    ...
    };

    #endif

    ------Next file

    #ifndef CPASSENGER_H
    #define CPASSENGER_H

    class cPassenger
    {
    ...
    cShip * current_vehicle;
    ...
    };

    #endif

    Now the problem I have is how to join these files together with includes
    such that they compile. (The idea here is that a ship always has an
    owner but that a passenger can be in a ship that they don't own).

    Sorry if this is a really mundane question,
    Connell
     
    Connell Gauld, Jun 2, 2005
    #1
    1. Advertising

  2. Connell Gauld wrote:
    > I have what feels like a really stupid question and I'm sorry if it is
    > asked a lot.
    > [...circular dependency question snipped...]


    Don't be sorry, just read the FAQ before posting.
    You can find it here: http://www.parashift.com/c -faq-lite/

    V
     
    Victor Bazarov, Jun 2, 2005
    #2
    1. Advertising

  3. An owner may not be a passenger, so it may make more sense to have a
    separate cOwner and cPassenger class. But to get circular references to
    compile, you can simply use a forward declaration:

    #ifndef CSHIP_H
    #define CSHIP_H

    class cPassenger;

    class cShip
    {
    ...
    cPassenger * owner;
    ...
    };

    #endif

    #ifndef CPASSENGER_H
    #define CPASSENGER_H

    class cShip;

    class cPassenger
    {
    ...
    cShip * current_vehicle;
    ...
    };

    #endif

    This could cause you some problems though if any part of the class
    declaration relies on deeper knowledge about the forward declared
    class. A forward declaration only gives you the name of the class, not
    any details about data members or member functions.
     
    James Daughtry, Jun 2, 2005
    #3
  4. * Connell Gauld:
    >
    > I have what feels like a really stupid question and I'm sorry if it is
    > asked a lot.


    Not at all, it's a good question.

    There are a host of very _similar_ questions that all involve
    circular dependencies.

    In your case the usual solution, forward declarations, is _not_
    appropriate.


    > Imagine I have two classes cShip and cPassenger. They each have a
    > definition in their own header cShip.h and cPassenger.h and their
    > implementation in cShip.cpp and cPassenger.cpp. Now here is the header
    > file for each:
    >
    > #ifndef CSHIP_H
    > #define CSHIP_H
    >
    > class cShip
    > {
    > ...
    > cPassenger * owner;
    > ...
    > };
    >
    > #endif
    >
    > ------Next file
    >
    > #ifndef CPASSENGER_H
    > #define CPASSENGER_H
    >
    > class cPassenger
    > {
    > ...
    > cShip * current_vehicle;
    > ...
    > };
    >
    > #endif
    >
    > Now the problem I have is how to join these files together with includes
    > such that they compile. (The idea here is that a ship always has an
    > owner but that a passenger can be in a ship that they don't own).


    What you have is a design problem, not (just) a technical circular
    dependency problem.

    Let the owner of a ship be a cPerson.

    Let cPassenger be a class derived from cPerson, and voilà, problem solved;
    you then have

    cShip depends on CPerson
    cPassenger depends on cShip and cPerson

    Later on you might consider the case where the owner might be a person _or_
    a corporation.

    With that twist it gets more interesting... ;-)

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, Jun 2, 2005
    #4
  5. Connell Gauld wrote:
    ....
    >
    > Now the problem I have is how to join these files together with includes
    > such that they compile. (The idea here is that a ship always has an
    > owner but that a passenger can be in a ship that they don't own).


    Ownership (no pun intended) is somthing you need to manage with the
    code, not the declaration (if you were using smart pointers you could do
    this otherwise).

    As for the declaration issues - you need to forward declare the class
    you're referring to - see below.

    #ifndef CSHIP_H
    #define CSHIP_H

    class cPassenger; // declare a class cPassenger

    class cShip
    {
    ...
    cPassenger * owner;
    ...
    };

    #endif

    ------Next file

    #ifndef CPASSENGER_H
    #define CPASSENGER_H

    class cShip; // declare a class cShip

    class cPassenger
    {
    ...
    cShip * current_vehicle;
    ...
    };
     
    Gianni Mariani, Jun 2, 2005
    #5
  6. Alf P. Steinbach wrote:
    > * Connell Gauld:
    >
    >>I have what feels like a really stupid question and I'm sorry if it is
    >>asked a lot.

    >
    >
    > Not at all, it's a good question.
    >
    > There are a host of very _similar_ questions that all involve
    > circular dependencies.
    >
    > In your case the usual solution, forward declarations, is _not_
    > appropriate.


    Huh?

    > [...]
     
    Victor Bazarov, Jun 2, 2005
    #6
  7. * Victor Bazarov:
    > Alf P. Steinbach wrote:
    > > * Connell Gauld:
    > >
    > >>I have what feels like a really stupid question and I'm sorry if it is
    > >>asked a lot.

    > >
    > >
    > > Not at all, it's a good question.
    > >
    > > There are a host of very _similar_ questions that all involve
    > > circular dependencies.
    > >
    > > In your case the usual solution, forward declarations, is _not_
    > > appropriate.

    >
    > Huh?


    See the rest of that message, but, since you ask, some background:

    It's generally not a good idea to solve design problems by applying
    technical kludges to the _symptoms_, whether the kludges be forward
    declarations, 'friend', 'void*', C-style casts, or whatever.

    Such "solutions" come back to haunt you, and also they typically
    yield more complex, non-maintainable code in the first place, because you
    missed out on sensible abstraction opportunities (which simplify things).

    Cheers,

    - Alf

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, Jun 2, 2005
    #7
  8. Alf P. Steinbach wrote:
    > * Victor Bazarov:
    >
    >>Alf P. Steinbach wrote:
    >>
    >>>* Connell Gauld:
    >>>
    >>>
    >>>>I have what feels like a really stupid question and I'm sorry if it is
    >>>>asked a lot.
    >>>
    >>>
    >>>Not at all, it's a good question.
    >>>
    >>>There are a host of very _similar_ questions that all involve
    >>>circular dependencies.
    >>>
    >>>In your case the usual solution, forward declarations, is _not_
    >>>appropriate.

    >>
    >>Huh?

    >
    >
    > See the rest of that message, but, since you ask, some background:
    >
    > It's generally not a good idea to solve design problems by applying
    > technical kludges to the _symptoms_, whether the kludges be forward
    > declarations, 'friend', 'void*', C-style casts, or whatever.


    What makes you think there *is* a design problem there? The need to use
    a "technical kludge" is *not* necessarily an indication of a design flaw.
    Just like 'friend' and 'void*' and 'dynamic_cast', forward declarations
    are there to be used to _accommodate_ certain design decisions, not to be
    avoided at all costs.

    > Such "solutions" come back to haunt you, and also they typically
    > yield more complex, non-maintainable code in the first place, because you
    > missed out on sensible abstraction opportunities (which simplify things).


    No! What you did suggest was, in fact, a kludge. If two classes do
    depend on each other, the model in question is _fine_, it is perfectly
    OK to have a pointer to A in B and a pointer to B in A. And, yes, the
    only clean way to resolve the reference "problems" is in the FAQ.

    Introducing a non-existent design elements just to avoid using some
    language features you might be finding "inappropriate" for whatever
    reason, is definitely a mistake.

    Design comes first, language-specific implementation comes second.

    V
     
    Victor Bazarov, Jun 2, 2005
    #8
  9. * Victor Bazarov:
    >
    > Design comes first, language-specific implementation comes second.


    Right.

    I gather all that about likes and dislikes that I snipped abvoe, a
    completely unwarranted speculation about my thinking (I don't think that
    way), was really about your earlier posting, posted almost at the same time
    as mine, where you failed to notice it was a design problem... ;-)

    Check the design again, then tell me it's reasonable that a boat's owner
    must be a passenger.

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, Jun 2, 2005
    #9
  10. "Alf P. Steinbach" wrote:
    >

    [snip]
    >
    > Let the owner of a ship be a cPerson.


    OK.
    (Side note: Every cPerson owns a ship? I don't think so.
    So I take it for granted that you wanted to have a cPerson class
    and derive a COwner class from it)

    >
    > Let cPassenger be a class derived from cPerson, and voilà, problem solved;
    > you then have
    >
    > cShip depends on CPerson
    > cPassenger depends on cShip and cPerson
    >
    > Later on you might consider the case where the owner might be a person _or_
    > a corporation.


    Well. Eventually he will come to the point where his ships actually transport
    cPassenger-s.

    So this will end up in

    cShip depends on COwner and cPassenger
    cOwner depends on cShip
    cPassenger depends on cShip

    Same problem. The design is better, but the circular dependency stays
    the same.

    --
    Karl Heinz Buchegger
     
    Karl Heinz Buchegger, Jun 2, 2005
    #10
  11. Alf P. Steinbach wrote:
    > * Victor Bazarov:
    >
    >>Design comes first, language-specific implementation comes second.

    >
    >
    > Right.
    >
    > I gather all that about likes and dislikes that I snipped abvoe, a
    > completely unwarranted speculation about my thinking (I don't think that
    > way), was really about your earlier posting, posted almost at the same time
    > as mine, where you failed to notice it was a design problem... ;-)


    *What* design problem?

    > Check the design again, then tell me it's reasonable that a boat's owner
    > must be a passenger.


    You're trying to read more than the OP has ever thought of presenting.
    Whether the design is reasonable is _not_ the topic of this discussion.
    For all it's worth, the question could be about A having a B* in it and
    B having an A*.

    *You* read the original post again. It doesn't say "My boat ownership
    model demands to have two classes 'cPassenger' and 'cShip' and here is
    the relationship between them". It says "IMAGINE I have two classes..."
    (emphasis added).

    I don't dispute that there can be design flaws. What I am disputing that
    the need to use a forward declaration is an automatic indication (symptom)
    of a bad design.

    Besides, if you want to talk design, perhaps you should invite the OP to
    comp.object? He asked for a language-specific solution and he got it.

    V
     
    Victor Bazarov, Jun 2, 2005
    #11
  12. * Victor Bazarov:
    > It says "IMAGINE I have two classes..." (emphasis added).


    So, the example is what one should imagine, then. ;-)


    > I don't dispute that there can be design flaws.


    Good, we're converging toward agreement here.


    > What I am disputing that the need to use a forward
    > declaration is an automatic indication (symptom) of a bad design.


    There we agree completely! :)

    Now who the ... said that?

    Give him to me (I've got my hammer ready); I'll bash him!


    > Besides, if you want to talk design, perhaps you should invite the OP to
    > comp.object? He asked for a language-specific solution and he got it.


    As you wrote earlier,


    > Design comes first, language-specific implementation comes second.



    Using a language correctly or reasonably has very much to do with the design
    level. Otherwise we wouldn't frown on 'void*' and the like. Remove the
    design level and you could as well use only the 'asm{ ... }' feature.

    So, IMO the design level is the first one should focus on except when a
    problem is stated without any connection to an application, e.g. like your
    A/B example, and I think you agree with that.

    Cheers,

    - Alf

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, Jun 2, 2005
    #12
  13. * Karl Heinz Buchegger:
    > "Alf P. Steinbach" wrote:
    > >

    > [snip]
    > >
    > > Let the owner of a ship be a cPerson.

    >
    > OK.
    > (Side note: Every cPerson owns a ship? I don't think so.
    > So I take it for granted that you wanted to have a cPerson class
    > and derive a COwner class from it)


    Nope.

    That "nope" follows from the minimalist guideline.

    If there isn't a problem, don't solve it (yet), and there isn't a problem:
    a ship can have a person as owner without every person being an owner.

    A cOwner class is only necessary if an owner has some extra attributes
    or different behavior than a person.

    So far nothing has indicated that owners do, but in the OP's model
    passengers have, compared to persons.


    > > Let cPassenger be a class derived from cPerson, and voilà, problem solved;
    > > you then have
    > >
    > > cShip depends on CPerson
    > > cPassenger depends on cShip and cPerson
    > >
    > > Later on you might consider the case where the owner might be a person _or_
    > > a corporation.

    >
    > Well. Eventually he will come to the point where his ships actually transport
    > cPassenger-s.
    >
    > So this will end up in
    >
    > cShip depends on COwner and cPassenger
    > cOwner depends on cShip
    > cPassenger depends on cShip
    >
    > Same problem. The design is better, but the circular dependency stays
    > the same.


    No, and yes.

    Different but in some respects similar problem: different solution.

    Details matter, IMHO.

    I think it's interesting that with cPerson (I don't like prefixes!)
    one can also more easily see a solution to this new slightly different
    problem, where one detail is exemplified by: "what class should contain the
    embark() member function?". Should we write ship.embark(passenger) or
    should we write passenger.embarkOn(ship), or what?

    I think I'd land on the "or what", at least as fundamental implementation;
    when thinking about the design level a person isn't forever a passenger,
    it's just a transient property, and it can even be an implied property that
    shouldn't then be represented explicitly in the cPerson objects except as an
    optimization if it turns out that optimization is necessary.

    General summary: circular dependencies sometimes arise from bad design,
    and generalizing the model to cover more is one possible way to remove such
    a dependency (not always applicable, but often enough).

    The first generalization, to add the Person concept; the second, to extend
    the model to cover cases where persons aren't passengers all the time.

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, Jun 2, 2005
    #13
  14. Alf P. Steinbach wrote:
    > [...]
    > So, IMO the design level is the first one should focus on except when a
    > problem is stated without any connection to an application, e.g. like your
    > A/B example, and I think you agree with that.


    Well, no. You need to remember that C++ is a multi-paradigm language.
    OO is not the end of its application. You also need to remember that
    we are in a _language_ newsgroup, and while design is important, and it
    does come first, when asked a *simple* language question, one shouldn't
    instead go off on a tangent and begin discussing design when no design
    discussion is requested.

    It's all about assumptions. I (and a couple of other folks) assumed
    that the OP knows what he's doing, and while there can always be some
    not-so-obvious misconceptions somebody might have about the model he is
    using, it's up to him to recognize that and ask for comments on it. You
    OTOH assumed that the OP does *not* know what he's doing. Now, while it
    is up to the OP to decide whose assumption is the correct one (and I am
    not asking about it 'coz I really don't care), with all other things
    being equal, a plain answer to the question posed is _better_ than any
    other off-on-a-tangent speculation.

    If you don't think so, fine. If you reluctantly agree, fine. If you
    now see the light and admit your off-on-a-tangent-ness, fine. In any
    case, I am not interested in discussing this any more, at least here.
    I've stated my point of view, explaining it further makes no sense to me.

    Thanks for reading.

    V
     
    Victor Bazarov, Jun 2, 2005
    #14
  15. * Victor Bazarov:
    > Alf P. Steinbach wrote:
    > > [...]
    > > So, IMO the design level is the first one should focus on except when a
    > > problem is stated without any connection to an application, e.g. like your
    > > A/B example, and I think you agree with that.

    >
    > Well, no. You need to remember that C++ is a multi-paradigm language.
    > OO is not the end of its application. You also need to remember that
    > we are in a _language_ newsgroup, and while design is important, and it
    > does come first, when asked a *simple* language question, one shouldn't
    > instead go off on a tangent and begin discussing design when no design
    > discussion is requested.


    One should give the best available answer, and here that was design.

    That's not going off on a tanget: it's ordaining the best medicine.

    This medicine also happens to be a general technique not AFAIK mentioned in
    the FAQ, so it's interesting in its own right.


    > It's all about assumptions. I (and a couple of other folks) assumed
    > that the OP knows what he's doing, and while there can always be some
    > not-so-obvious misconceptions somebody might have about the model he is
    > using, it's up to him to recognize that and ask for comments on it. You
    > OTOH assumed that the OP does *not* know what he's doing.


    Naturally; he or she wouldn't ask if he or she knew.

    Do you see that now?


    > Now, while it
    > is up to the OP to decide whose assumption is the correct one (and I am
    > not asking about it 'coz I really don't care), with all other things
    > being equal, a plain answer to the question posed is _better_ than any
    > other off-on-a-tangent speculation.


    What I gave was the plainest, best answer.

    Advicing to use forward declarations, void* pointers or whatever
    tecnical kludges that can hide symptoms, but not cure, and that
    can and probably will exacerbate the problems, is ungood.

    Especially when the cure is so exceedingly simple.

    Cheers,

    - Alf

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, Jun 2, 2005
    #15
  16. Alf P. Steinbach wrote:
    > [...]
    > Especially when the cure is so exceedingly simple.


    ROFLMAO Yeah, right...
     
    Victor Bazarov, Jun 2, 2005
    #16
  17. * Victor Bazarov:
    > Alf P. Steinbach wrote:
    > > [...]
    > > Especially when the cure is so exceedingly simple.

    >
    > ROFLMAO Yeah, right...


    Sharpen up, Victor.

    It _is_ exceedingly simple.

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, Jun 2, 2005
    #17
  18. Alf P. Steinbach wrote:
    > * Victor Bazarov:
    >
    >>Alf P. Steinbach wrote:
    >>
    >>>[...]
    >>>Especially when the cure is so exceedingly simple.

    >>
    >>ROFLMAO Yeah, right...

    >
    >
    > Sharpen up, Victor.
    >
    > It _is_ exceedingly simple.


    ROFLMAO To whom?

    While I have to admit this *is* amusing... I got no time for this.
     
    Victor Bazarov, Jun 2, 2005
    #18
  19. shouldn't the declaration and implementation be in the header?
    richard
    "Connell Gauld" <> wrote in message
    news:d7n3cs$v36$...
    > Hi,
    > I have what feels like a really stupid question and I'm sorry if it is
    > asked a lot.
    > Imagine I have two classes cShip and cPassenger. They each have a
    > definition in their own header cShip.h and cPassenger.h and their
    > implementation in cShip.cpp and cPassenger.cpp. Now here is the header
    > file for each:
    >
    > #ifndef CSHIP_H
    > #define CSHIP_H
    >
    > class cShip
    > {
    > ...
    > cPassenger * owner;
    > ...
    > };
    >
    > #endif
    >
    > ------Next file
    >
    > #ifndef CPASSENGER_H
    > #define CPASSENGER_H
    >
    > class cPassenger
    > {
    > ...
    > cShip * current_vehicle;
    > ...
    > };
    >
    > #endif
    >
    > Now the problem I have is how to join these files together with includes
    > such that they compile. (The idea here is that a ship always has an owner
    > but that a passenger can be in a ship that they don't own).
    >
    > Sorry if this is a really mundane question,
    > Connell
     
    richard pickworth, Jun 4, 2005
    #19
  20. In message <>, Alf P. Steinbach
    <> writes
    >
    >It's generally not a good idea to solve design problems by applying
    >technical kludges to the _symptoms_, whether the kludges be forward
    >declarations, 'friend', 'void*', C-style casts, or whatever.


    Hmm.

    Friend's a kludge because it breaks encapsulation.
    void* and casts are kludges because they break type safety.

    But what does a forward declaration break?

    --
    Richard Herring
     
    Richard Herring, Jun 8, 2005
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. MS News \(MS ILM\)
    Replies:
    1
    Views:
    426
    Scott Schluer
    Aug 19, 2003
  2. Xavier MT
    Replies:
    1
    Views:
    607
    Scott Schluer
    Aug 19, 2003
  3. Maxwell Hammer
    Replies:
    7
    Views:
    658
    Peter Hansen
    Jun 18, 2005
  4. Raman
    Replies:
    5
    Views:
    382
    Lew Pitcher
    Jun 7, 2008
  5. Nathan Beyer
    Replies:
    0
    Views:
    121
    Nathan Beyer
    Nov 15, 2009
Loading...

Share This Page