I am still new to C++ and don't have a great insight on my coding yet, so I would be very grateful to anyone and everyone that gives advice.
Also, this code is meant to: keep all of my objects in an ordered fashion based on depth. I have a couple functions that allow for easy management and I made object
friends with the depth manager
because I only want the depthManager
to have control over each objects idDepth
and depth
which are two different things.
The reason I need a depth system is because I need to have objects execute there code in an orderly fashion, and I also need to have control of what objects are drawn first to last.
This class has been tested and works as expected.
Abstract object
class:
class object{
// Placement Data
unsigned int depth, idDepth, idObject, idMain;
// Friends
friend class depthManager;
friend class objectManager;
protected:
unsigned int getDepthId(){ return idDepth; }
public:
virtual void update() = 0;
virtual void draw() = 0;
unsigned int getDepth() { return this->depth; }
};
note: I have each object handle its own update and draw events for easier design. I'm mainly asking on approval for the depth system.
depthManager
class
class depthManager{
private:
std::map<unsigned, std::vector<object*>* > objectMap;
void changeListPlacement( unsigned int depth, unsigned int position, int change);
public:
void objectAdd( unsigned int depth, object* obj);
void objectRemove( object* obj );
void objectMove( unsigned int depth, object* obj );
};
depthManager
s Functions:
void depthManager::objectAdd(unsigned int depth, object *obj)
{
obj->depth = depth;
// Check if depth key existant
if ( objectMap.find( depth ) != objectMap.end() )
{
std::vector< object* >* &refVec = objectMap[ depth ];
refVec->push_back( obj );
obj->idDepth = (unsigned)(int)refVec->size() - 1;
}
else // Add new Key
{
objectMap[ depth ] = new std::vector< object* >;
std::vector< object* >* &refVec = objectMap[ depth ];
refVec->push_back( obj );
obj->idDepth = (unsigned)(int)refVec->size() - 1;
}
}
void depthManager::changeListPlacement(unsigned int depth, unsigned int position, int change = -1)
{
if ( objectMap.find( depth ) == objectMap.end() )
{
return;
}
std::vector<object*>* &refVec = objectMap[ depth ];
for( unsigned int i = refVec->size() - 1; i > position; i -- )
{
object* &pObj = refVec->at( i );
pObj->idDepth += change;
}
}
void depthManager::objectRemove(object *obj)
{
if ( objectMap.find( obj->depth ) == objectMap.end() )
{
std::cout << "ERROR DEPTH NOT FOUND \n" << std::flush ;
return;
}
std::vector<object*>* &refVec = objectMap[ obj->depth ];
changeListPlacement( obj->depth, obj->idDepth);
refVec->erase( refVec->begin() + obj->idDepth );
}
void depthManager::objectMove(unsigned int depth, object *obj)
{
this->objectRemove( obj );
this->objectAdd( depth, obj );
}
previous verions
1 Answer 1
Some remarks to your current map
-based implementation:
The idiomatic way of accessing an item in a map and adding it if not present usually goes like this:
if not map.contains(key) map[key] = new value value = map[key] ... modify value
So
objectAdd
could be shortened:obj->depth = depth; // make sure we have an object vector for the given depth if ( objectMap.find( depth ) == objectMap.end() ) { objectMap[ depth ] = new std::vector< object* >; } std::vector< object* >* &refVec = objectMap[ depth ]; refVec->push_back( obj ); obj->idDepth = (unsigned)(int)refVec->size() - 1;
In
objectRemove
it is apparently illegal to pass an object with a non-existent depth. Printing an error message to stdout is not the best way to handle an error like that. You should throw an appropriate exception (fail early is a valuable debugging tool) or allow it by ignoring invalid depths.It is not imminently clear what
objectMove
exactly does based on its name and the names of its parameters. It looks like it moves an object to a different depth. So a better name and signature might be:void depthManager::changeDepth(unsigned int newDepth, object *obj)
In
class object
the methodsgetIdDepth()
andgetDepth
do not modify the object state so you should consider making themconst
(i.e.unsigned int getDepth() const { return this->depth; }
).I'm not 100% convinced of the
idDepth
property. It basically just reflects the current position of the object in the depth-list and you are writing a fair amount of boiler plate code to keep it that way. This increases the complexity of the class somewhat. I'd revisit the concept and check if I can't get by without it.
-
\$\begingroup\$ Great catch on point #1! It definitely should be simplified. On #2 I have been semi rushing this code, I haven't sat down to learn error handling yet; but I do intend to add that functionality. #3 when I made that function I was uncertain on the naming convention, changeDepth does sound more reasonable. #4 This functionality was replace with const getDepth() earlier today, and yes it should be read only. #5 All the Id's are merely positions in a corresponding vector, this way looping is cut to a minimum. --- I greatly Appreciate the insight! \$\endgroup\$Andrew– Andrew2013年12月16日 07:58:37 +00:00Commented Dec 16, 2013 at 7:58
-
\$\begingroup\$ @Lemony-Andrew On #1:
map::operator[]
default constructs and inserts an element for the provided key if none exists. So if default construction of values is okay, the idiomatic form is justauto& value = map[key]
. In the OP's case, where the value type is a raw pointer, you can just addif (!value) value = new Value
to create the new value object, but that's ignoring the questionable usage of raw pointers with ownership semantics. Anyway, this method only involves 1 tree search, instead of the 3 required for acontains()
and 2operator[]
calls. \$\endgroup\$bcrist– bcrist2013年12月16日 09:55:07 +00:00Commented Dec 16, 2013 at 9:55 -
\$\begingroup\$ @bcrist Alright I'll have a look at the code later today. Also were you using auto for shortening purposes to explain the code? Else I see no benefits in using it in my case. The IDE wouldn't like to work with it haha ( doesn't know the type so no friendly auto-completion ) \$\endgroup\$Andrew– Andrew2013年12月16日 14:07:29 +00:00Commented Dec 16, 2013 at 14:07
-
\$\begingroup\$ @Lemony-Andrew Yes, the
auto&
was just for ease of reading the comment. Personally I have no problem usingauto
in a program that's already using other C++11 features, and Visual Studio intellisense has no problem determining the actual type of anauto
, but I try to only use it where the entire type would be much harder to read; eg.auto
is much easier to read thanstd::unordered_map<Identifier<SomeType>, SomeType, SomeTypeHasher<Identifier<SomeType>>, std::equal_to<Identifier<SomeType>>, SomeAllocatorTypedef>::const_iterator
\$\endgroup\$bcrist– bcrist2013年12月17日 14:56:01 +00:00Commented Dec 17, 2013 at 14:56
Explore related questions
See similar questions with these tags.
object
s ordered bydepth
? What are the use cases, besides iterating though all objects in all depths in either order? \$\endgroup\$std::map<unsigned, std::vector<object*>* > objectMap;
\$\endgroup\$