Reputation: 4191
I have a Container
class, which has been designed with an item type, declared in the header and conditionally defined in the cpp-file:
Container.h:
struct Container
{
Container();
// other members
private:
struct ITEM;
ITEM* table;
};
Container.cpp:
#include "Container.h"
#if CONTAINER_TYPE == 1
struct Container::ITEM
{
// some data fields
};
#elif CONTAINER_TYPE == 2
struct Container::ITEM
{
// some data fields
};
#else
#error "..."
#endif
Container::Container()
{
table = new ITEM[100];
}
Now I want to get rid of conditional compilation, and instead to use some combination of templates and inheritance to choose the item type during the Container
construction:
// main.cpp
#include "Container.h"
int main()
{
Container c(1);
// and so on
}
My requirements:
The ITEM
type shouldn't contain vtable
, cause the ITEM
size and performance are extremely important - I'm talking about really large containers.
The template mechanics should begin and end in the cpp-file, cause I don't want to disclose too much know-how to customers - only headers will be accessible to them.
What are my options? Are there any design patterns, applicable here?
Upvotes: 1
Views: 105
Reputation: 597036
If you want the user of the container to decide the type of item used at runtime, while still hiding the implementation details, you will have to do something more like this:
struct Container
{
enum ItemType { eItemType1, eItemType2 };
Container(ItemType aItemType);
~Container();
// other members
private:
struct ITEM {
virtual ~ITEM() {}
};
ITEM** table;
};
#include "Container.h"
struct ItemType1 : Container::ITEM
{
// some data fields
};
struct ItemType2 : Container::ITEM
{
// some data fields
};
Container::Container(Container::ItemType aItemType)
{
table = new ITEM*[100];
for (int i = 0; i < 100; ++i)
{
switch (aItemType)
{
case eItemType1:
table[i] = new ItemType1;
break;
case eItemType2:
table[i] = new ItemType2;
break;
default:
table[i] = new ITEM;
break;
}
}
Container::~Container()
{
for (int i = 0; i < 100; ++i)
delete table[i];
delete[] table;
}
#include "Container.h"
int main()
{
Container c(eItemType1);
// and so on
}
If you absolutely cannot have a vtable in ITEM
, and the table content must be held in sequential memory, then you will have to do something more like this instead:
struct Container
{
enum ItemType { ItemType1, ItemType2 };
Container(ItemType aItemType);
~Container();
// other members
private:
void* table;
ItemType type;
};
#include "Container.h"
struct ItemType1
{
// some data fields
};
struct ItemType2
{
// some data fields
};
Container::Container(Container::ItemType aItemType)
{
type = aItemType;
switch (aItemType)
{
case eItemType1:
table = new ItemType1[100];
break;
case type2:
table = new ItemType2[100];
break;
default:
table = NULL;
break;
}
}
Container::~Container()
{
switch (type)
{
case eItemType1:
delete[] static_cast<ItemType1*>(table);
break;
case type2:
delete[] static_cast<ItemType2*>(table);
break;
}
}
#include "Container.h"
int main()
{
Container c(eItemType1);
// and so on
}
Upvotes: 2
Reputation: 766
I don't think you have many options if you want to hide the template semantics from your customers. Using templates is easy though. If you wanted to have the data type of the stored data as template arguments it could look something like this:
template <typename ITEM>
struct Container
{
Container(ITEM a) {
//not sure what you want to do here
table = new ITEM[100];
}
// other members
private:
//the pointer to the stored array
ITEM* table;
};
int main()
{
Container<int>(1);
}
Notice that the only thing I did was to make ITEM a template argument. Now you can define which type of data your Container should have when you create an instance of it.
Furthermore if you are looking at performance I don't see why you wouldn't just use a pointer to the data. This way you keep the overhead low and the elements don't get copied each time you copy the container object.
Might not be the best solution but you could start with that. Let me know what you had in mind.
Upvotes: 0