Инициализируйте ссылочную переменную членов класса, как если бы она была обычной переменной - PullRequest
5 голосов
/ 26 февраля 2011

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

struct object{
  float & xPosition;
  float & yPosition;
  float & zPosition;
  ...
  object(float & xPos, float& yPos, float& zPos):xPosition(xPos), yPosition(yPos), zPosition(zPos){}
  object():xPosition(*new float(0.0f)), yPosition(*new float(0.0f)), zPosition(*new float(0.0f)){}
};

Однако это приведет к утечкам памяти, поскольку эти значения не удаляются. Есть ли у вас какие-либо предложения о том, как я могу добиться желаемого поведения без утечки памяти?

EDIT

Я бы предпочел не использовать повышение. Однако я не против решения, которое требует шаблона. Кроме того, из-за того, что это частично является оптимизацией производительности, boost :: shared_ptr, кажется, не является правильным решением.

Ответы [ 5 ]

5 голосов
/ 26 февраля 2011

Я бы предложил вам использовать boost::shared_ptr для этих структур позиций.Таким образом, вам не нужно беспокоиться об удалении, и вы можете использовать его как указатель, который используется совместно с объектом игрового движка, или как автономный указатель.

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

Например, вы можете получить следующее:

class object {
  public:
    typedef boost::shared_ptr<Vector3D> pVector3D;
  private:
    pVector3D position;
  public:
    object(pVector3D aPos = pVector3D(new Vector3D(0.0,0.0,0.0))) : position(aPos) { };
};

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

РЕДАКТИРОВАТЬ

Я бы предпочел не использовать повышение.Однако я не против решения, которое требует шаблона.Кроме того, из-за того, что это частично является оптимизацией производительности, boost :: shared_ptr, кажется, не является правильным решением.

Ну, shared_ptr / shared_array также можно найти в стандартной библиотекетехнический отчет 1 (TR1) (поэтому вместо него std::tr1::shared_ptr, поэтому вам не нужно использовать Boost для их использования).Что касается оптимизации производительности, поэтому я рекомендую довольно высокое соотношение данных к указателю.Накладные расходы на shared_ptr - это, в основном, накладные расходы памяти и некоторая косвенность при удалении и копировании (это две операции, которые выполняются не так часто), я не думаю, что при доступе к данным, на которые он указывает, слишком много накладных расходов по сравнению с обычнымиуказатель или ссылка.Вы должны признать, что даже используя ссылки, вы торгуете накладными расходами на копирование данных с накладными расходами на косвенный доступ к данным (вы также жертвуете локальностью памяти, что очень важно!).Я бы сказал, что падение производительности, связанное с нарушением локальности памяти, будет намного хуже, чем просто косвенное обращение.Таким образом, когда дело доходит до доступа к элементам, IMO, shared_ptr, необработанные указатели и ссылки будут иметь очень небольшую разницу в производительности.Во многих алгоритмах, которые используют эти общие переменные, вам, вероятно, было бы лучше скопировать данные, на которые указывает указатель / ссылка на локальные переменные, вычислить с этими локальными переменными и на них, а затем скопировать их обратно в память, указанную указателем/reference.

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

EDIT2

Рассматривали ли вы использование схемы множественного наследования.Эта проблема, вероятно, может быть очень хорошо решена с помощью схемы наследования алмазов:

class PositionedObject {
  protected:
    float Position[3];
  public:
    PositionedObject(float x,float y, float z) { Position[0] = x; ... };
    virtual ~PositionedObject() { };
};

class VisibleObject : virtual public PositionedObject { //note that the "virtual" keyword is critical here.
  ... rendering-related code ... i.e. the game-engine side of the implementation
};

class RigidBody : virtual public PositionedObject { //again "virtual" is very important.
  ... physics code here ...
};

class MyObject : public VisibleObject, public RigidBody {
  ... code specific to MyObject ...
};

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

3 голосов
/ 26 февраля 2011

Вы можете использовать Boost shared_array, чтобы разделить координаты XYZ между произвольным числом объектов:

struct object {
    boost::shared_array<float> coords;

    object(const boost::shared_array<float>& coords_)
        : coords(coords_)
    {
    }

    object()
        : coords(new float[3])
    {
        coords[0] = coords[1] = coords[2] = 0.f;
    }
}

Шаблоны shared_array и shared_ptr используют подсчет ссылок , чтобы гарантировать, что память будет удалена после уничтожения последней ссылки на нее. Создание копии shared_array или shared_ptr добавляет единицу к счетчику ссылок, а уничтожение shared_array или shared_ptr вычитает один из счетчиков ссылок. Когда счетчик ссылок достигает 0, общая память удаляется.

1 голос
/ 26 февраля 2011

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

struct object{
  float & xPosition;
  float & yPosition;
  float & zPosition;

  object(float & xPos, float& yPos, float& zPos)
    :xPosition(xPos),
     yPosition(yPos),
     zPosition(zPos),
     allocated(false)
  {}
  object()
    :xPosition(*new float(0.0f)),
     yPosition(*new float(0.0f)),
     zPosition(*new float(0.0f)),
     allocated(true)
  {}

  ~object() {
    if(allocated) {
      delete &xPosition;
      delete &yPosition;
      delete &zPosition;
    }
  }

private:
  bool allocated;
};
1 голос
/ 26 февраля 2011

Один очень простой способ ...

struct object
{
    float& xPosition;
    float& yPosition;
    float& zPosition;
    float x, y, z;
    ...

    object(float& xPos, float& yPos, float& zPos)
      : xPosition(xPos), yPosition(yPos), zPosition(zPos)
    { }

    object() : xPosition(&x_), yPosition(&y_), zPosition(&z_), x(0), y(0), z(0)
    { }
};

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

Если вы действительно хотите придерживаться чего-то более близкого к тому, что было у вас, вы можете просто добавить логическое значение, чтобы отслеживать, нужно ли памятибыть удаленным.(РЕДАКТИРОВАТЬ: PigBen только что отправил код для таких).Лично я бы порекомендовал использовать один new float[3], чтобы вы делали одно выделение памяти вместо трех ... не только это будет быстрее, но и тратить меньше памяти на управление кучей.

0 голосов
/ 26 февраля 2011

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

class physics_system_t {
public:
    std::vector<float *> my_arrays;
} physics_system;

class object {
public:
    object(float * coords_ptr, bool manage_memory) : coords_array(coords_ptr)
    {
        if (manage_memory) {
            physics_system.my_arrays.push_back(coords_ptr);
        }
    }
    float *coords_array;
};

РЕДАКТИРОВАТЬ: Вот еще одна альтернатива, которая еще не была предложена. Используйте объединение, чтобы хранить ваши данные как локальные переменные или ссылки, и иметь логическое значение для переключения между ними.

class object {
public:
    object(float & x, float& y, float& z, bool use_refs);
    union {
        struct refs_t {
            float &xr;
            float &yr;
            float &zr;
        } refs;
        struct vals_t {
            float x;
            float y;
            float z;
        } vals;
    }
    bool use_refs;
};

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...