альтернатива объединению для классов с ctors - PullRequest
0 голосов
/ 20 апреля 2011

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

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

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

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


class Employee
{
public:
int ID;
float Salary;
};</p>

<p>enum COMPARE_RESULT
{
   LESS_THAN = -1,
   EVEN = 0,
   GREATER_THAN = 1
}
template 
class IComparable
{
public:
virtual COMPARE_RESULT Compare (const T& other) const = 0;
};</p>

<p>class EmployeeCompareByID : public Employee, public IComparable
{
public:
Compare (const T& other) const
{
    //Compare IDs and return result
}
};</p>

<p>class EmployeeCompareBySalary : public Employee, public IComparable
{
public:
Compare (const T& other) const
{
    //Compare Salaries and return result
}
};</p>

<p>typedef union
{
    Employee employeeData;
    EmployeeCompareByID employeeCompareByID;
    EmployeeCompareBySalary employeeCompareBySalary;
}EmployeeUnion;</p>

<p>//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;</p>

<pre><code>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 есть перегрузка конструктора, конструктора копирования и оператора, что определяет объединениена сотрудника невозможно.Кроме того, этот подход требует, чтобы реализация Tree выполняла static_cast для шаблона-аргумента TYPE для IComparable, что мне кажется правильным.

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

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

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

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

Ответы [ 4 ]

2 голосов
/ 20 апреля 2011

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

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

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

template<class T> class Tree {
   Tree(const ICompare<T> &c) : c(c) { }
   ...
    const ICompare<T> &c;
};
1 голос
/ 20 апреля 2011

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

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

1 голос
/ 20 апреля 2011

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

Здесь вы просто хотите, чтобы дерево с ключом в одном случае идентификатором и вдругой - зарплата, и элемент - это указатель на сотрудника.

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

1 голос
/ 20 апреля 2011

Компаратор не работник - он не должен наследовать. Создайте отдельные компараторы, которые смотрят на переданные 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; 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...