alternative to union for classes with ctors
I have a design-related question in C++.
I'm building a tree for a HW assignment. The tree implementation is pretty straightforward. Basically it's a templated class
template <typename TYPE, typename KEY>
class Tree
{
public:
void Insert(TYPE& node);
void Remove(TYPE& node);
TYPE& Find (KEY key);
//etc.
};
So far is just background. Now, later when I use Tree, I have an Employee class that I want to have 2 trees of, but once use ID as the key and in the other use Salary as the key, but I don't want to duplicate the data. Obviously I need to implement 2 different comparison functions. My first attempt was to do something like this:
class Employee
{
public:
int ID;
float Salary;
};
enum COMPARE_RESULT
{
LESS_THAN = -1,
EVEN = 0,
GREATER_THAN = 1
}
template
class IComparable
{
public:
virtual COMPARE_RESULT Compare (const T& other) const = 0;
};
class EmployeeCompareByID : public Employee, public IComparable
{
public:
Compare (const T& other) const
{
//Compare IDs and return result
}
};
class EmployeeCompareBySalary : public Employee, public IComparable
{
public:
Compare (const T& other) const
{
//Compare Salaries and return result
}
};
typedef union
{
Employee employeeData;
EmployeeCompareByID employeeCompareByID;
EmployeeCompareBySalary employeeCompareBySalary;
}EmployeeUnion;
//finally the main would do something like this:
int main()
{
//first tree开发者_StackOverflow社区 key is by ID
Tree empTreeByID;
//second tree key is by salary
Tree empTreeBySalary;
EmployeeUnion emp;
emp.employeeData.ID = 1;
emp.employeeData.Salary = 1000.11;
empTreeByID.Insert(emp.employeeCompareByID);
empTreeBySalary.Insert(emp.employeeCompareBySlary);
//the same emp is referenced in both trees. Each by it's relevant member in union
}
but this approach fails because my Employee class has a constructor, copy constructor and operator overloading so defining a union on Employee is impossible. Furthermore, this approach requires the Tree implementation to do a static_cast on the TYPE template-argument to IComparable which seems fine to me.
Another possible solution is to pass a function-pointer to the Tree constructor, with a pointer to the appropriate Compare function for that instance, but this seems to me an inelegant solution, and possibly quite messy and hard to debug later.
Googling on the limitations of using unions with classes, I found a post suggesting to solve a problem similar to mine using Conversion operator overloading but didn't expand much.
I'm sure this is a fairly common problem that has a common design solution, and I just don't know it.
Any thoughts or comments are appreciated
The actual problem in your code is that you try to place the comparision and the employee data structure to the same object. They should be different objects like this:
template<class T> class ICompare {
virtual bool Compare(const T &a, const T &b) const=0;
};
and then obviously you need to modify the Tree constructor:
template<class T> class Tree {
Tree(const ICompare<T> &c) : c(c) { }
...
const ICompare<T> &c;
};
A comparator isn't an employee - it shouldn't inherit. Make separate comparators that look at the passed Employee
references and compare them.
If you don't want to duplicate data, use e.g. boost::shared_ptr
or this approach:
list<Employee> ActualData;
map<int, list<Employee>::iterator> EmployeeByID;
map<float, list<Employee>::iterator> EmployeeBySalary;
shared_ptr:
map<int, shared_ptr<Employee> > EmployeeByID;
map<float, shared_ptr<Employee> > EmployeeBySalary;
I doubt there is a good use case of unions except in very low-level code. It's almost always a bid idea because it can incure lots of undesired effects (google a bit to see that).
Here, you just want to have a tree with the key being in one case the ID and in the other the salary, and the item is a pointer to the employee.
That way you don't duplicate employees, you just have several pointers pointing to the employees, which is fine. And your two trees are separate, which is desirable. (And if you want to add any other tree you can do the same without changing the other trees types.)
as already said, i'd also go with a multi index container and some sort of a smart pointer to the employee object. something along the lines of (from the top of my head, not tested!):
typedef multi_index_container<
boost::shared_ptr < Employee >,
indexed_by<
// sort by unique id
ordered_unique < tag < EmployeeIdTag >, member < Employee,int,&Employee::m_id > >,
// sort by salary
ordered_non_unique < tag < EmplyeeSalaryTag >, member < Employee,int,&Employee::m_salary > >
>
> EmployeeContainer;
typedef EmployeeContainer::index<EmployeeIdTag>::type EmployeeContainerById;
typedef EmployeeContainer::index<EmplyeeSalaryTag>::type EmployeeContainerBySalary;
http://www.boost.org/doc/libs/1_46_1/libs/multi_index/doc/examples.html#example1
精彩评论