So there's got to be a good standard way of doing this, but I'm not seeing it and I'm wondering how others would handle this.
lets say for example I have a State object and I want to create a City object. In order to uniquely identify the city it has a state object inside of it and to create the City object you pass your state object into the constructor.
State my_state;
try{
my_state.init("MN");
}catch(InvalidState&){
return;
}
City my_city1;
try{
my_city1.init(my_state, "Minneapolis");
}catch(InvalidCity&){
return;
}
City my_city2;
try{
my_city2.init(my_state, "St. Paul");
}catch(InvalidCity&){
return;
}
How would you hold the state object on the city objects? Would you have each city object own it's own copy of state? if so what if I called a method on City called increment_population(1) which increased the population of city by one, and intern called an increment_population(1) method on the state object as well. If I called that method on my_city2 it would only increment the state object local to the my_city2, but my_city1 would have an incorrect state population.
The way we're doing it is to hold a pointer to state on the city. So everything is pointing to the same object. The problem is what happens if someone default constructs a city and doesn't call init. If I call the get_state_name() method on city which intern calls state->name() I could end up in a world of hurt. I don't want to put a check on every getter of city which calls a method of state to make sure state is not null though either.
Well, you say "constructor" of the City object, but what you show is not related to constructors.
Funny enough, a constructor is your solution, I would say:
1 2 3 4 5 6 7 8 9 10 11 12 13
class City
{
private:
State *_theState;
public:
City(State *theState) : _theState(theState)
{
if (!_theState)
{
//Throw exception.
}
}
};
If you only provide a constructor that demands a state, then you are good to go, IMO.
Note, however, that State object creation need to be regulated. I suggest a static method in State that creates State objects: static GetState(LPCTSTR state);, where LPCTSTR comes from my Windows background. It is a const pointer to char or wchar_t if you use Unicode.
I had a feeling that's the response i'd get. The problem is that we are trying to tighten the scope of where errors are thrown as well. to do it without default construction and init means it would have to look like this.
1 2 3 4 5 6 7 8 9 10 11 12 13
try{
State my_state("MN");
City my_city(my_state,"Minneapolis");
all the code that uses city for something.....
}catch(InvalidState&){
}catch(InvalidCity&){
}
The idea is to keep the handling of an exception as close as possible to where the exception is actually getting thrown. You can argue over whether that's necessary or not, but without going into more detail about it we've decided that's important.
So the question is how do you allow default construction of an object without having to check for initialization in every getter which accesses the data?
It is the same for constructors, except that you will have to use a pointer:
1 2 3 4 5 6 7 8
City *pCity = 0;
try
{
pCity = new City(my_state, "Minneapolis");
}
catch (InvalidCity&)
{ }
print(pCity->name());
Of course it would be bad to use raw pointers because you lose automatic destruction, so granted: It is not really the same. But that can be very easily fixed with a CityHolder class. Actually, make the constructor of City private and make CityHolder a friend class of City, and create all cities through the CityHolder class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
class CityHolder
{
City *_theCity;
public:
CityHolder() : _theCity(0)
{ }
~CityHolder()
{
if (_theCity) delete _theCity; //Or not. Don't if you somehow handle singletons.
}
void CreateCity(State *pState, LPCTSTR cityName)
{
_theCity = new City(pState, cityName);
}
City& operator->()
{
return *_theCity;
}
};
I have never overloaded operator->, but that should work, I think. If I recall correctly, CComPointer<> overloads it like this to allow access to the contained object.