2015-05-14 6 views
0

У меня есть проблема, которую я не могу решить. Я попытался найти аналогичный вопрос здесь, но не нашел для меня рабочего решения.Функция автоматического возврата функции в C++

Моя структура:

class Base 
{ 
    unsigned int ID; 
}; 

class Position: public Base 
{ 
    float x,y; 

    Position(float a, float b): x(a), y(b) {} 
} 

class Mass: public Base 
{ 
    float mass; 

    Mass(float a): mass(a) {} 
} 

хранить указатели на все атрибуты в карте accesed со строками. Я хотел бы иметь функцию, которая может возвращать любой атрибут из этого списка, используя имена. Структура и желаемая функциональность должна быть такой:

std::map<string, Base*> attributes; 

???? getAtt(string name) 
{ 
    return attributes[name]; 
} 

Position pos(1,2); 
Mass mass(25.6); 
attributes.emplace("TEST_POSITION", &pos); 
attributes.emplace("TEST_MASS") &mass); 

cout << "Mass of this object is " <<getAtt("TEST_MASS").mass << endl; 
cout << "X - Position of this object is " << getAtt("TEST_POSITION").x ; 

PRINTS: Mass of this object is 25.6 
     X - Position of this object is 1 

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

+0

Вы намеренно смешиваете атрибуты разных типов? Часто бывает проще иметь несколько однородных контейнеров, чем один гетерогенный контейнер. – MSalters

ответ

0

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

template <typename T> 
T& getAtt(string name) 
{ 
    return dynamic_cast<T&>(*attributes.at(name)); 
} 

Edit: использовать at вместо [], [] имеет побочный эффект, который он создает несуществующие ключи.

А потом называют это так:

getAtt<Mass>("TEST_MASS").mass 
getAtt<Position>("TEST_POSITION").x 

Однако, этот код будет кошмаром для отладки. Старайтесь избегать общих атрибутов и использовать сильно типизированные переменные, т. Е. вместо:

std::map<string, Base*> attributes; 
attributes.emplace("mass", Mass(…)); 
attributes.emplace("position", Position(…)); 

использование:

Mass mass; 
Position position; 
+0

Спасибо, что шаблон работает. Я хотел бы добавить эти атрибуты во время выполнения, поэтому мне нужна эта карта. Потому что я могу добавлять и удалять из него атрибуты. Я хотел сделать небольшое физическое моделирование со звездами - гравитацией и такими вещами. И я хотел иметь его красиво упакованным и удобочитаемым. – Quimby

+0

Шаблоны @Quimby - это функция времени компиляции, они не могут быть созданы во время выполнения, поэтому, что бы вы ни делали с ними, вы также можете использовать сильные типизированные переменные. Кроме того, вы очень скоро заметите, что общие атрибуты могут быть всем, но определенно не упакованы или легко читаемы. – StenSoft

+0

@Quimby: Зачем добавлять произвольные атрибуты, когда код не знает, что с ними делать? И если вы добавите код для обработки имитирующих атрибутов, вы можете добавить код для чтения атрибутов. –

0

Ваш getAtt возвратит Base *, как это:

Base* getAtt(const string& name) 
{ 
... 
} 

Но базовый класс не предоставляет интерфейс для всех его производным класса, поэтому вы не можете просто делать

вместо этого вы должны сделать это:

dynamic_cast<Mass*>(getAtt("TEST_MASS"))->mass; 
dynamic_cast<Position*>(getAtt("TEST_POSITION"))->x; 

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

Кстати, оператор оператора [] карты создаст элемент, если он не существует, поэтому вам нужно проверить, что getAtt не возвращает nullptr.

0

Вы можете вернуть ссылочный объект, у которого есть оператор преобразования, к типу. Если преобразование выполняется с динамическим приведением, результат будет NULL, если будет предпринята попытка присвоить объект тому, что он не является. Для динамического переноса требуется Base виртуального метода (вы можете просто добавить виртуальный деструктор).

class Base { 
    unsigned int ID; 
protected: 
    virtual ~Base() {} 
}; 

class Ref { 
    friend Ref getAtt (std::string name); 
    Base *ref_; 
    Ref (Base *p = 0) : ref_(p) {} 
public: 
    template <typename D> operator D *() const { 
     return dynamic_cast<D *>(ref_); 
    } 
}; 

Ref getAtt (std::string name) { return attributes[name]; } 

Этот метод не позволяет лечить Ref в качестве какого-либо конкретного типа. Это позволяет вам назначить его тому, что ему разрешено стать.

Mass *m = getAtt("TEST_MASS"); 
Position *p = getAtt("TEST_POSITION"); 
cout << "Mass of this object is " << m->mass << endl; 
cout << "X - Position of this object is " << p->x ; 

Если результирующий указатель NULL, это означает, что либо элемент не существует, или вы пытаетесь присвоить его неправильные вещи.

Position *p = getAtt("TEST_MASS"); 
assert(p == NULL); 
+0

Спасибо, что выглядят очень красиво. Я обязательно попробую :) – Quimby

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