Multiple types in a map container C++

Started by
11 comments, last by Soundstone 10 years, 11 months ago

Hi everyone,

not sure if this is the proper thread to post this question to, if not I apologize and will gladly post it where it needs to be. I am having an issue with understanding how to use multiple data types in one map container. For example my code looks like this:

struct A
{
string mName;
int mNumber;
};
struct B
{
string mName;
float mFloater;
bool mFlag;
};
class C
{
C();
~C();
//basically I want this map to hold a list by reference int for the entire collection of both objects from A and B.
map<int, A/B> myMultiMap;
}
so that in main..
int main()
{
C* myC;
A* myA;
B* myB;
myC->myMultiMap[0] = myA;
myC->myMultiMap[1] = myB;
}
is there any easy way to do this. I haven't had much luck with getting the boost libraries to load correctly, I've only been at this about a year, and don't completely know how to do some of the things. I appreciate any help that is offered, even ifs its just a link or something. Thanks for reading.
Regards,
Soundstone
Advertisement
Here's one option:
struct Base
{
    virtual ~Base() { }
};

struct Alpha : public Base
{
    int Foo;
};

struct Beta : public Base
{
    std::string Bar;
};

int main()
{
    Alpha* someAlpha = new Alpha;
    someAlpha->Foo = 42;

    Beta* someBeta = new Beta;
    someBeta->Bar = "Test";

    std::map<int, Base*> someMap;
    someMap[0] = someAlpha;
    someMap[1] = someBeta;
}
A better design would be to use std::shared_ptr (or boost::shared_ptr if your compiler doesn't support the C++11 standard) instead of raw pointers.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

You still need some way to know what type of object it actually is though... you can use dynamic_cast for that if you want.

Can you only have one of type A or type B corresponding to a particular int key? Because if that is not the case you are probably better off having 2 maps, one holding A's and the other holding B's.

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

You still need some way to know what type of object it actually is though... you can use dynamic_cast for that if you want.

I disagree.


In a well-designed system, you should use virtual dispatch to "do things" with the Alpha/Beta objects. You should virtually never need to know its concrete type, and dynamic_cast is a code smell.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Yeah, I agree, but with the empty base class implementation it would be required.

If we knew more about what the OP wanted we could probably suggest a better design I feel.

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

Some other options that I don't see being mentioned here, if in fact A and B should not inherit from the same base class:

  • use "void *" as the value type for your map (though this gets tricky, because you need to know the type when you pull the object and dynamic_cast it)
  • use "boost::any" as the value type for your map (this is a little better but can cause problems if you have exceptions off because boost throws an execption if boost::any_cast fails)

A better design would be to use std::shared_ptr (or boost::shared_ptr if your compiler doesn't support the C++11 standard) instead of raw pointers.

As for this point...wouldn't you say this advice is a bit misleading? Yes, shared pointers have their place, but shouldn't the train of thought be "by default, try to make sure a pointer is owned by only one object, and if that is impossible or breaks the design of the application, then use a shared pointer"?

Then use a std::unique_ptr, problem solved.

My point wasn't to make a blanket rule for all pointer usage (so I'm not sure why you read that into my post) but rather to point the OP in the direction of smart pointers as a preference over raw pointers.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Thanks for advice guys, Ill try and make it more clear what I am trying to accomplish.

In visualization terms think of a box, this box can hold exactly 9 objects in it,each of the 9 spots is a numbered index 0-8, but only objects of either type A or type B. I would like a associative container that can keep track of which everything in the box, including which type is located at which box index. I tried doing this through inheritance and downcasting but I feel like there should be a better way. I have data members in A and B that extend a baseclass. The classes look similar to this:


Class BaseClass
{
public:
	BaseClass(string, int, string, int, int, int);
	~BaseClass();
	void SetName(string value) 	{name = value;}
	void SetType(int value)		{type = value;}
	void SetFlavor(string value)	{flavor = value;}
	void SetNum1(int value)		{num1 = value;}
	void SetNum2(int value)		{num2 = value;}
	void SetNum3(int value)		{num3 = value;}
	const string GetName()		{return name;}
	const int  	GetType()	{return type;}
	const string	GetFlavor()	{return flavor;}
	Const int	GetNum1()	{return num1;}
	const int	GetNum2()	{return num2;}
	const int	GetNum3()	{return num3;}
	
protected:
	string  name; //name of object
	int	type; //flagged 0 for type a and 1 for type B
	string  flavor; //flavor text about object
	int     num1; 
	int 	num2;
	int 	num3;
}

Class A : public BaseClass
{
public:
	A(string name, int type, string flavor, int num1, int num2, int num3, int num4
              int num5, float speed)
          :BaseClass(name, type, flavor, num1, num2, num3);
	~A();
	//getters and setters for speed, num4 and num5...

private:
	float speed;
	int 	num4;
	int 	num5;
}

Class B : public BaseClass
{
public:
	B(string name, int type, string flavor, int num1, int num2, int num3 int bInt)
          :BaseClass(name, type, flavor, num1, num2, num3);
	~B();
	void SetBInt(int value)		{bInt = value;}
	const int GetBInt()		{return bInt;}
private:
	int bInt;
}

so that when in class box it would look like this:


//Box.h

#include "A.h"
#include "B.h"
#include "BaseClass.h"
Class Box
{
public:
	Box();
	~Box();
	void AddObjectTypeA(A* objectA);
	void AddObjectTypeB(B* objectB);
private:
	map<int, BaseClass*> myBoxMap;
}


//Box.cpp

void Box::AddObjectTypeA(A* objectA)
{
	bool full = false;
	for(int i = 0; i < myBoxMap.size(); i++)
	{
		if(i == myBoxMap.size())
		{
			//map is full - only want 9 objects inside no more
			full = true;
			break;
		}
		if(mFoundOffensiveMap->GetOffCellName() != "") //preloaded during box construction with 9 objects inside of type Basecell name = ""
		{
			//bad choice cell full already
			continue;
		}
		else if(mFoundOffensiveMap->GetOffCellName() == "")
		{
			//good cell choice - it's empty currently
			myBoxMap->SetName(cell->GetName());
			myBoxMap->SetType(cell->GetType());
			myBoxMap->Setnum1(cell->Getnum1());
			myBoxMap->Setnum2(cell->Getnum2());
			myBoxMap->Setnum3(cell->Getnum3());
			myBoxMap->Setnum4(cell->GetNum4());
			myBoxMap->setSpeed(cell->GetSpeed());
			myBoxMap->setnum5(cell->Getnum5());
			
			
			break;
		}
	}
	return full;

}

finally in main.cpp



//main.cpp

int main()
{
	A* myObjA = new A("Steve", 0, "is a man", 4, 6, 7, 17, 8, 3.4f);
	Box* myBox;

	myBox->AddObjectA(myObjA);
				 
	/* during the above call I would like to set myObjA into the Box 
	   at the first available index without another object. It tests
	   this in the function by checking if name = "". */
}

I hope that better explains it. As it currently stand I'm getting access violation errors when in the function to add. Those errors are keeping me from knowing if this set up will actually work for what I want. Thank you again, for reading into this. I truly appreciate the quick responses you have offered.

Regards,
Soundstone

The access violations would be because you use a pointer to a Box (Box* myBox) but you don't create an object of type B by doing

Box* myBox = new Box();

But you are probably better off just having a Box local variable (or member variable of a class) so you don't have to create it with operator new in the first place.

"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

This topic is closed to new replies.

Advertisement