2015-01-03 3 views
-1

У меня есть массив, который является списком FIFO и к которому я добавляю свои записи данных.
Моя запись данных может быть любого из стандартных типов (строка, char, int, long, unsigned, float, double), которые я нажимаю на свой массив, используя функцию.
Я хочу позже прочитать эту запись данных, чтобы они были добавлены.Один массив, несколько типов

Это то, что я пробовал:

class List 
{ 
    typedef std::pair<typename, std::vector<char>> Record; // typename ?? 

    public: 
     template <typename T> 
     void addRecord(T value) 
     { 
      char* arr = reinterpred_cast<char*>(&value); // Casting ? 
      // Convert to Record and push to _records 
     } 

     template <typename T> 
     T getRecord(std::vector<Record>::iterator record) const 
     { 
      // Convert Record to T and return 
     } 

    private: 
     std::vector<Record> _records; 
} 

Как конвертировать из этих типов в байты массива или есть другой способ сделать это?

Пример пути я хочу использовать это:

List list; 
list.addRecord("Test string"); 
list.addRecord(10); 
list.addRecord(999999); 
list.addRecord("Test string 2"); 
list.addRecord('X'); 
... 

А потом читать их точно так же:

std::string testString = list.getRecord(...); 
char testChar = list.getRecord(...); 
int testInt = list.getRecord(...); 
.... 
+1

'string' не является базовым типом, который сохранится в этом виде лечения в целом. Почему вы не используете 'boost :: variant'? –

+0

Использование union или void * – texasbruce

+0

boost - вещь в этом случае, но, к сожалению, я забыл упомянуть, что одним из моих требований к проекту является использование только стандартных библиотек. – user3065410

ответ

2

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

Для целых типов это достаточно для управления, так как вам нужно обеспечить примитивную поддержку для установки и извлечения значения. Для более сложных типов, таких как std::string и std::vector, все становится немного сложнее, если вы не используете C++ 11. Это связано с тем, что до C++ 11 union не может содержать типы, которые имеют либо нетривиальный конструктор копирования, нетривиальный деструктор, либо нетривиальный оператор присваивания копии.

Оба примера any классы, указанные в комментариях (here и here), кажутся разумными и полными реализациями, но требуют C++ 11. Оба полагаются только на компоненты в стандартной библиотеке, чтобы они могли быть жизнеспособными решениями для вас. Однако, если вы не используете C++ 11 или вам требуется более простое решение для вашего решения, я предоставил образец ниже. Он обрабатывает char, int и double, поэтому, если вам нужно поддерживать более сложные типы, вам необходимо их добавить. В случае сложных типов (и не C++ 11) вам нужно будет удерживать указатель на экземпляр и управлять временем жизни самостоятельно (вручную или с помощью умного указателя). Вам также потребуется обработать копирование и назначение на основе ваших конкретных потребностей (глубокие или мелкие копии).

Простой меченого союз: использование

struct TaggedUnion 
{ 
    enum Type 
    { 
     Char, 
     Int, 
     Double 
    }; 


    TaggedUnion(const char& value) : type(Char), value(value) {} 
    TaggedUnion(const int& value) : type(Int), value(value) {} 
    TaggedUnion(const double& value) : type(Double), value(value) {} 

    Type getType() const { return type; } 
    char getChar() const { assert(type == Char); return value.getChar(); } 
    int getInt() const { assert(type == Int); return value.getInt(); } 
    double getDouble() const { assert(type == Double); return value.getDouble(); } 


private: 

    union Union 
    { 
     Union(const char& value) : charValue(value) {} 
     Union(const int& value) : intValue(value) {} 
     Union(const double& value) : doubleValue(value) {} 

     char getChar() const { return charValue; } 
     int getInt() const { return intValue; } 
     double getDouble() const { return doubleValue; } 

    private: 

     char charValue; 
     int  intValue; 
     double doubleValue; 
    }; 

    Type type; 
    Union value; 
}; 

Пример:

#include <iostream> 
#include <vector> 
#include <cassert> 

int main() 
{ 
    std::vector<TaggedUnion> values; 

    values.push_back(TaggedUnion(0.0)); // Store double/float 
    values.push_back(TaggedUnion(0));  // Store int 
    values.push_back(TaggedUnion(' '));  // Store char 
} 
+0

Это похоже на отличную идею! Я пошел в действительно, действительно неправильном направлении, поскольку я никогда не использовал профсоюзы. Кажется, он решает все мои проблемы, и поскольку я использую C++ 11, и, как вы сказали, поддерживаются нетривиальные типы, такие как строка, и это прекрасно! – user3065410

0

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

class Value { 
public: 
    virtual ~Value() {} 

    // Define operations needed for values, e.g. 
    virtual void print() = 0; 
}; 

class IntValue: public Value { 
public: 
    IntValue(int val) : m_val(val) {} 

    virtual void print() { 
     std::cout << m_val; 
    } 

private: 
    int m_val; 
}; 

class StringValue: public Value { 
public: 
    StringValue(const std::string& val) : m_val(val) {} 

    virtual void print() { 
     std::cout << m_val; 
    } 

private: 
    std::string m_val; 
}; 

// Equivalent class definitions for other value types. 

Затем, чтобы создать коллекцию значений:

std::vector<Value*> values; 
values.push_back(new IntValue(42)); 
values.push_back(new StringValue("Hello")); 

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

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

+0

Категорически не согласен. Это прекрасный пример плохого «религиозного» проекта ООП. Теперь, когда вы хотите добавить некоторые новые функции в принимающий конец очереди, вы должны добавить его как метод для каждого класса. Если вы когда-нибудь захотите получить основные данные, вам все равно придется реализовать что-то вроде меченого союза. Эта конструкция заставляет очередь истекать в другие части системы, которые не должны знать об этом. – japreiss

+0

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

+0

@japreiss Это действительно зависит от конкретного случая. Пока вам просто нужно хранить основные типы, я бы не использовал это. Но в более общей теме, как хранить несколько типов в массиве, который является названием вопроса, стоит представить оба варианта. В большинстве случаев в реальном мире это более масштабируемый подход. –

Смежные вопросы