Хранить объекты трех разных классов в массиве

#c

#c

Вопрос:

Это вопрос из интервью: как мы можем хранить объекты, скажем, 3 разных классов, которые полностью независимы друг от друга.

Мой ответ был таким: создайте массив, в котором хранятся все указатели void. Нравится :

 void *array[];
  

и сохранять указатели на все объекты.элементов массива может быть много.
но я сказал, что для извлечения элементов мы можем использовать динамическое приведение или статическое приведение!
я думаю, это неправильный ответ. я думаю, что dynamic cast и static cast предполагается использовать среди зависимых классов.
пожалуйста, поправьте меня, если я ошибаюсь.

если динамическое приведение и статическое приведение не работают.Может быть, мы можем использовать reinterpret cast .

но правильный ли это способ выполнить эту задачу?

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

1. @Rahul Dravid Тебе нужен кортеж.

2. Если вам нужно это сделать, вам следует использовать безопасное решение, подобное boost::any .

3. @Red Hue: (1) почему (??) и (2) как

4. его не интересовал boost.

5. @Angel: Нет, C не имеет UDT, определенных с помощью ключевого слова class . Однако в C вообще есть пользовательские типы (см.: struct ), и поэтому этот вопрос был бы применим к обоим языкам… если бы не {dynamic,static,reinterpret}_cast ссылки.

Ответ №1:

Почему вы пытаетесь сделать это таким сложным?

 struct T {
   T1 obj1;
   T2 obj2;
   T3 obj3;
};
  

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

(Да, вы бы подумали, reinterpret_cast если бы были вынуждены использовать этот подход из-за ограниченных возможностей трудоустройства и острой необходимости потреблять пищу.)

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

1. если это было глупо — зачем были boost::any или boost::variant созданы?

2. Если бы вы пошли по пути без повышения, по крайней мере, сделали бы это различимым объединением? Я знаю, что существуют (строгие) ограничения на то, какие типы могут использоваться в качестве членов объединения, но многие из этих ограничений сняты с появлением c 0x

3. @Nim: Чтобы люди могли обойти глупые проекты. Вы собираетесь утверждать, что само существование вещи означает, что требования, которые приводят к ее использованию, должны быть не глупыми?

4. @sehe, контекст важен, но отвергать что-либо как «глупое» без контекста — это просто глупо, ИМХО. @Tomalak, конечно, нет, я хочу сказать, что само существование подразумевает, что была / есть какая-то ситуация, когда это было / уместно, и делать общее заявление, отвергающее подход, «глупо».

5. @Konstantin: Дочитайте до конца en.wikipedia.org/wiki/Argument_from_silence и en.wikipedia.org/wiki/Argument_from_ignorance

Ответ №2:

Вы могли бы использовать a boost::variant<myfoo, anotherfoo, somefoo> v для помещения в a vector<v> , а затем вы можете хранить по одному классу на элемент в векторе, однако он может хранить переменные всех трех типов.

Вы можете найти больше информации о boost::variant здесь

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

1. он ожидал ответа, не использующего какую-либо библиотеку, подобную boost.

2. @Rahul, о, хорошо … не знал. Однако это могло бы сработать, IRL

Ответ №3:

dynamic_cast из void* не допускается, для этого вам нужен полиморфный указатель (то есть указатель на класс с virtual методом в нем).

Вы не можете использовать другие приведения, потому что целевой тип приведения неизвестен. Как только вы приводите типизированный указатель на void* , информация о типе теряется навсегда. Учитывая только указатель, у вас нет шансов получить информацию об этом типе обратно. Вам нужно помнить что-то о типе где-то в какой-то форме. Как насчет enum { one, two, three } наряду с void* ?

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

1. reinterpret_cast было бы неплохо.

2. @Tomalak reinterpret_cast<???> Что ты пишешь внутри скобок? На выбор предлагается три типа.

Ответ №4:

Единственный «правильный» способ сделать это — использовать что-то вроде boost::variant . Если вы не можете использовать boost, тогда вам придется более менее делать то же самое: реализовать различающее объединение, которое принимает все желаемые типы. Это не так сложно, как может показаться: для типов POD вы можете просто использовать normal union для данных; для типов, отличных от POD, вы добавляете unsigned char x[sizeof(T)] к объединению и используете для него размещение new и явное удаление по мере необходимости. И вы добавляете все необходимое к объединению, чтобы обеспечить выравнивание. Таким образом, для пользовательского типа MyClass и double может быть что-то вроде:

 class MyVariant
{
public:
    enum Type { t_double, t_MyClass };
private:
    Type m_type;
    union
    {
        double m_double;
        unsigned char m_MyClass[sizeof(MyClass)];
        MaxAlignFor<MyClass> m_dummyForAlignment_MyClass;
    };
public:
    MyVariant( double d )
        : m_type( t_double )
    {
        m_double = d;
    }
    MyVariant( MyClass constamp; c )
        : m_type( t_MyClass )
    {
        new (m_MyClass) MyClass( c );
    }
    ~MyVariant()
    {
        switch ( m_type )
        {
        case t_double:
            break;

        case t_MyClass:
            reinterpret_cast<MyClass*>( m_MyClass )->~MyClass();
            break;
        }
    }
};
  

И так далее. (Очевидно, что требуется гораздо больше, но это должно дать
базовая платформа.)