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 21 1382
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
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.
* 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?
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;
...
};
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: 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 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: 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" 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 kb******@gascad.at
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: 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?
* 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 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: 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 wrote: [...] Especially when the cure is so exceedingly simple.
ROFLMAO Yeah, right...
* 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 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.
shouldn't the declaration and implementation be in the header?
richard
"Connell Gauld" <co*****@freebreakfast.co.uk> wrote in message
news:d7**********@newsg4.svr.pol.co.uk... 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
In message <42*****************@news.individual.net>, Alf P. Steinbach
<al***@start.no> 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 wrote: In message <42*****************@news.individual.net>, Alf P. Steinbach <al***@start.no> 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.
It's only a kluge if you use it incorrectly. See FAQ 14.2.
void* and casts are kludges because they break type safety.
Yep, that's why the C++ cast operators are so ugly. :)
But what does a forward declaration break?
You'll often see a forward-declared class used to solve the problem of
mutual inclusion, i.e., when two headers #include each other. The
forward declaration allows it to compile, but more often than not
you're looking at a design problem.
Kristo
In message <11**********************@g14g2000cwa.googlegroups .com>,
Kristo <kr*******@gmail.com> writes Richard Herring wrote: In message <42*****************@news.individual.net>, Alf P. Steinbach <al***@start.no> 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.
It's only a kluge if you use it incorrectly. See FAQ 14.2.
Indeed. I was just trying to find a reason why it might be called a
kludge at all. void* and casts are kludges because they break type safety.
Yep, that's why the C++ cast operators are so ugly. :)
But what does a forward declaration break?
You'll often see a forward-declared class used to solve the problem of mutual inclusion, i.e., when two headers #include each other. The forward declaration allows it to compile, but more often than not you're looking at a design problem.
At that design level, yes, there may in some cases (I think "more often
than not" is too strong) be a problem with mutual *inclusion*.
But it's much more frequently used to solve legitimate problems of
mutual *reference*, cases where objects of two mutually-dependent
classes genuinely need to contain pointers or references to each other,
but nothing more; perhaps the most obvious is when a child needs to know
who its parent is.
But that wasn't really my point. The forward declaration itself doesn't
break anything[*]; on the contrary, it conveys the minimum possible
information about a class, hiding all the unnecessary detail. I'd say
that was a good and useful thing to be able to do, and it's unfair to
give it the same derogatory label as tricks involving casting to void*.
[*] except (in a different sense of the word) mutual-inclusion loops!
--
Richard Herring This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics |
by: WindAndWaves |
last post by:
Hi Gurus
I hope I am going to make sense with this question:
I have an html page that I have turned into a php page with a bit of php code above the html (connect to database, massage data a...
|
by: David |
last post by:
How can set of XML files be validated by one XML schema? To be more details, for instance, I created a SML schema with complex type Customer and Order in Orders.xsd. Table Order is related to...
|
by: bettervssremoting |
last post by:
To view the full article, please visit
http://www.BetterVssRemoting.com
Better VSS Remote Access Tool including SourceOffSite, SourceAnyWhere
and VSS Remoting
This article makes a detailed...
|
by: nrhayyal |
last post by:
Hi c++ Gurus,
Need your blessing.
while testing few aspects with respect to header file inclusions, i
observed few things which i would like to share with you.
i have a file sqlca.h in which a...
|
by: Cyberwolf |
last post by:
Hi all
I'm having a strange problem when upgrading the content management
system eZ publish from PHP 4.4 to PHP 5.1. Some cache files related to
i18n are included with the "include" function,...
| |
by: Steven Nagy |
last post by:
Hi all,
ASP.NET : Framework 2.0 - C#
A recent addition to my code generater will create GridView's and
ObjectDataSource's in a control (ASCX).
So the code gen creates an ascx, ascx.cs,...
|
by: Sieira |
last post by:
I have the following files:
netfunc.h:
typedef struct{
char data;
int n;
}packet;
|
by: Hongyu |
last post by:
Dear all:
I am trying to write to a file with full directory name and file name
specified (./outdir/mytestout.txt where . is the current directory)
in
C programming language and under Unix, but...
|
by: KIRAN |
last post by:
Hello all,
My question is about the way of including header files(*.h) in source
files (*.c)
I have three folders,
-build ( for project makefiles)
-include ( for *.h files)
-src (for *.c...
|
by: Oralloy |
last post by:
Hello folks,
I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>".
The problem is that using the GNU compilers,...
|
by: jinu1996 |
last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
| |
by: Hystou |
last post by:
Overview:
Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
|
by: agi2029 |
last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development projectplanning, coding, testing,...
|
by: isladogs |
last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM).
In this session, we are pleased to welcome a new...
|
by: conductexam |
last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...
|
by: TSSRALBI |
last post by:
Hello
I'm a network technician in training and I need your help.
I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs.
The...
|
by: muto222 |
last post by:
How can i add a mobile payment intergratation into php mysql website.
| |
by: bsmnconsultancy |
last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence...
| |