A tricky OOP problem I never got my head around
Say I have two .cpp files: oranges.cpp and basket.cpp. They have the classes orange
and basket
, respective开发者_StackOverflow社区ly. My main
program generates many baskets
which in turn generate many oranges
. So basically, main
will have many objects of Baskets
; and baskets
will have many objects of Oranges
. If I have a function in orange
that needs to know the color of my basket, how would I go about finding the color of the basket?
orangle.cpp
class oranges
{
void whichColorBasket()
{
// get the color of the basket the orange is in...?
}
}
basket.cpp
class basket
{
int color;
void basket()
{
for(int i = 0; i < 10; i++)
oranges *o = new oranges;
}
}
I know my syntax may not be perfect but how would I access the datamember of basket
from a function in orange
(orange
is an object created by basket
).
Sending the color a parameter isn't an option as there are too many orange
s and the color of the basket
may change during runtime.
I read somewhere that static functions would do the trick, but they only work if they are in the same .cpp file.
So, what do I do?
Static functions are almost certainly not the answer here.
You would probably need to pass a reference to the "parent" Basket
to the Oranges
object, which it can then interrogate to find the colour.
For example:
class Basket
{
public:
void Basket() {
Oranges *o = new Oranges(*this);
}
color getColor() const { ... }
};
class Oranges
{
private:
const Basket &myBasket;
public:
Oranges(const Basket &b) : myBasket(b) {} // Store reference
color getColor() const { return myBasket.getColor(); } // Interrogate my basket
};
Whether you use a pointer or a reference will depend on whether the Oranges
can ever move between Basket
s.
you'll need a reference in orange pointing to the basket the orange is in
class oranges{
basket* b;
int whichColorBasket() {
return b->color;
}
}
class basket{
int color;
void basket(){
for(int i=0;i<10;i++){
oranges *o=new oranges;
o->b = &this;
}
}
}
I'm ignoring memory leaks here
You need to somehow bind oranges to the basket they belong to - for example, by passing a pointer to the basket. How you do it will depend on whether oranges can change their binding to a backet during their lifetime and whether backet can be destroyed before all oranges in it are destroyed.
Assuming oranges never outlive the basket and never change the basket you do it this way:
class oranges{
public:
oranges( backet* object ) { belongsTo = object; }
void whichColorBasket() {
here get belongsTo->color;
}
private:
backet* belongsTo;
};
class basket{
int color;
void basket(){
for(int i=0;i<10;i++)
oranges *o=new oranges( this ); //pass "current object"
}
};
Your oranges
object should have an instance variable basket
representing the basket it's in. Add a method putInBasket
which takes a basket and sets the variable to the basket it's in. Then the orange looks at the variable in the whichColorBasket
method. If basket
is NULL
it's not in a basket.
This isn't the best design, though, cause it provides a potential for inconsistency. A basket might think it has an orange, but the orange's basket
pointer could point to a different basket. Should an orange
really know the color of the basket it's in? What's the use case? If you're only handling baskets, and you have an orange, perhaps the basket should have a isOrangeHere
method, which tells you whether a given orange is there. You call it on all the baskets, and then you take the color of the one that returns true.
The simple answer is that you don't. If you have to, there is a problem in your design somewhere: what kind of orange would know what basket it is in? And what if the orange isn't in a basket?
If you need to support this in some way, the "proper" solution would be something along the lines of the Observer pattern; your fruit would be an observer of its container; when you put the fruit into a container, the container would register with the fruit, and when you took the fruit out, the container would deregister. Clients could then ask the fruit for their current container, and ask it whatever they want.
Add a property to oranges that contains a reference to the parent basket. In the method for adding oranges to basket, also set the parent to the basket.
class oranges{
basket* basket;
void oranged(basket* bask)
{
basket = bask;
}
int whichColorBasket() {
return basket->color;
}
}
class basket{
int color;
void basket(){
for(int i=0;i<10;i++)
oranges *o=new oranges(&this);
}
}
This syntax may be wrong but this is how its normally done.
Since you're using C++ use a pointer in your oranges. int *basket_color;
then just assign it the address of the baskets color basket_color = &color
The way to avoid the coupling is to ask each basket if it contains a specific orange. If it does, check its color.
Why should the orange care about the basket's color? What about the apples in the other basket? Are they interested too?
Your oranges
class could have a basket
member variable that references the basket that contains it. Then, when an oranges
object wants to know the colour of its containing basket it just calls gets the value of myBasket.color
.
The oranges
constructor would have to initialise this member variable, so it would need a basket
parameter.
EDIT: Completely redid this solution as I had missed on a key constraint.
Another possible way would be to add a member variable to the orange
class like so:
class oranges
{
private:
int m_nBasketID;
public:
oranges(int nID = 0)
{
m_nBasketID = nID;
}
void whichColorBasket()
{
return gBasketList[m_nBasketID].color;
}
}
And to set the value while creating the oranges
in the basket
class, like so:
class basket
{
static unsigned int nBasketID;
public:
int color;
void basket()
{
//First basket will have ID = 1 because
//0 is reserved for unknown/uninitialized state.
nBasketID++;
for(int i=0;i<10;i++)
oranges *o=new oranges(nBasketID);
gBasketList.push_back(*this);
}
Additional modifications are as follows:
unsigned int basket::nBasketID = 0;
std::vector<basket> gBasketList;
I've made a couple of assumptions here:
- There is a known value (such as zero) that can be used to represent an unknown/uninitialized colour state. This value can be used a default value in the constructor of
oranges
. - The
oranges
do not switch baskets after creation. If this use-case needs to be addressed then may you could delete theorange
from the originalbasket
and add a new one to the destinationbasekt
or provide a member function to handle this case etc.
精彩评论