альтернатива объединению для классов с ctors

#c #data-structures #tree

#c #структуры данных #дерево

Вопрос:

У меня есть вопрос, связанный с дизайном в C .

Я строю дерево для назначения HW. Реализация дерева довольно проста. По сути, это шаблонный класс

 template <typename TYPE, typename KEY>
class Tree
{
public:
void Insert(TYPEamp; node);
void Remove(TYPEamp; node);
TYPEamp; Find (KEY key);
    //etc.
};
  

Пока это просто фон.
Теперь, позже, когда я использую Tree, у меня есть класс Employee, для которого я хочу иметь 2 дерева, но один раз использовать ID в качестве ключа, а в другом использовать Salary в качестве ключа, но я не хочу дублировать данные.
Очевидно, мне нужно реализовать 2 разные функции сравнения. Моей первой попыткой было сделать что-то вроде этого:

 
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 Tamp; other) const = 0; };

class EmployeeCompareByID : public Employee, public IComparable { public: Compare (const Tamp; other) const { //Compare IDs and return result } };

class EmployeeCompareBySalary : public Employee, public IComparable { public: Compare (const Tamp; 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 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
  

}

но этот подход терпит неудачу, потому что мой класс Employee имеет перегрузку конструктора, конструктора копирования и оператора, поэтому определение объединения для Employee невозможно.
Кроме того, этот подход требует, чтобы реализация дерева выполняла static_cast для ТИПА
шаблон-аргумент для IComparable, который мне кажется подходящим.

Другим возможным решением является передача указателя на функцию конструктору дерева с указателем на соответствующую функцию сравнения для этого экземпляра, но это кажется мне неэлегантным решением и, возможно, довольно запутанным и трудным для последующей отладки.

Погуглив об ограничениях использования объединений с классами, я нашел сообщение, предлагающее решить проблему, похожую на мою, с помощью перегрузки оператора преобразования, но оно не сильно расширилось.

Я уверен, что это довольно распространенная проблема, которая имеет общее дизайнерское решение, и я просто не знаю этого.

Приветствуются любые мысли или комментарии

Ответ №1:

Реальная проблема в вашем коде заключается в том, что вы пытаетесь поместить comparision и структуру данных employee в один и тот же объект. Они должны быть разными объектами, подобными этому:

 template<class T> class ICompare {
  virtual bool Compare(const T amp;a, const T amp;b) const=0;
};
  

и затем, очевидно, вам нужно изменить конструктор дерева:

 template<class T> class Tree {
   Tree(const ICompare<T> amp;c) : c(c) { }
   ...
    const ICompare<T> amp;c;
};
  

Комментарии:

1. Похоже, это именно то решение, которого я пытался избежать (только вместо использования указателей на функции вы инкапсулировали функцию Compare в собственный класс, что лучше). Но из других ответов я понял, что без использования boost это, по-видимому, самый простой способ реализовать такое поведение)

Ответ №2:

Компаратор не является employee — он не должен наследоваться. Создайте отдельные компараторы, которые просматривают переданные Employee ссылки и сравнивают их.

Если вы не хотите дублировать данные, используйте, например, boost::shared_ptr или этот подход:

 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; 
  

Комментарии:

1. как указано, boost не разрешен, поскольку это назначение HW

Ответ №3:

Я сомневаюсь, что существует хороший вариант использования объединений, за исключением очень низкоуровневого кода. Это почти всегда выгодная идея, потому что она может вызвать множество нежелательных эффектов (немного погуглите, чтобы увидеть это).

Здесь вы просто хотите иметь дерево, ключом которого в одном случае является ID, а в другом — зарплата, а элемент является указателем на сотрудника.

Таким образом, вы не дублируете сотрудников, у вас просто есть несколько указателей, указывающих на сотрудников, и это нормально. И ваши два дерева разделены, что желательно. (И если вы хотите добавить любое другое дерево, вы можете сделать то же самое, не изменяя другие типы деревьев.)

Ответ №4:

как уже говорилось, я бы также выбрал контейнер с несколькими индексами и какой-нибудь интеллектуальный указатель на объект employee. что-то вроде (с моей точки зрения, не тестировалось!):

 typedef multi_index_container<
                        boost::shared_ptr < Employee >,
                        indexed_by<
                        // sort by unique id
                        ordered_unique < tag < EmployeeIdTag >, member < Employee,int,amp;Employee::m_id > >,
                        // sort by salary
                        ordered_non_unique < tag < EmplyeeSalaryTag >, member < Employee,int,amp;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