Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: ООП в С++6.0  (Прочитано 8013 раз)
0 Пользователей и 1 Гость смотрят эту тему.
ezus
Опытный

il
Offline Offline

« : 09-04-2014 08:21 » new

Добрый день!

Очередной затык в уставшей голове, а посоветоваться не с кем  - рядом все пишут на С#.

Проблема:
Есть массив интов. В нем группы ячеек описывают некий информационный объект. С этим объектом работают некоторые функции. Хотелось бы их локализовать в какой-нибудь структуре типа класса. Вся информация для работы с объектом храниться в этой последовательности ячеек.

Вопрос:
Можно ли (и, если можно, то  как) использовать интервал последовательных ячеек массива в качестве мембер-членов объекта некоторого класса?

Возможно существуют какие-то стандартные приемы? Или это полный бред?
Записан
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 09-04-2014 08:33 » 

ezus, а эти группы ячеек - они в массив каким образом попадают/распределяются/инициализируются?
Можно-то можно, даже placement new можно использовать. А для контейнеров (если полагать каждый твой информационный объект контейнером) - самописные аллокаторы памяти, что тоже, в некотором роде, подход стандартный.
Тут вопрос в поиске компромисса между универсальностью и простотой. Проще всего хранить в отдельном объекте 2 числа и указатель на массив, - но только пока не понадобится делать вставку в середину этого массива (или пока кто-то не упустит из виду, что этого делать нельзя).
Записан
ezus
Опытный

il
Offline Offline

« Ответ #2 : 09-04-2014 08:54 » 

Действительно - уточню.

Каша заваривается из-за памяти. Объекты множественно рождаются и удаляются. Стандартный аппарат выделения памяти в этом случае жрет непозволительно много времени. Поэтому очень хотелось бы избежать операторов "new".

Спасибо за "placement new" - раньше никогда не сталкивался. Наверное это то, что надо.
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 09-04-2014 10:20 » 

Вад, чего-то, по-моему, ты не в ту степь человека двигаешь.

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

Делается это элементарно двумя подходами:

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

2) Если нужны отдельные ячейки (отражение отдельных элементов массива в поля структуры), то в объекте объявляются членами поля типа int * или int &, а в конструкторе объекта соответствующие указатели/ссылки инициализируются, привязываясь к нужным ячейкам массива.

Главное условие при этом - неизменность куска памяти с исходным массивом, пока живы все привязанные к массиву объекты.

Если это условие не выполняется, то достаточно ввести косвенную адресацию (индексацию). Т.е. завести вспомогательный класс, внутри которого хранится индекс нужного элемента массива, и сам этот класс обеспечивает работу с собой как с обычной int-переменной, пряча внутри себя, что на самом деле при чтении и записи происходит обращение к массиву по числовому индексу. Из объектов такого класса уже можно формировать поля целевых объектов. Это даже производительность почти не снизит - не более, чем на обращение по указателю.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
ezus
Опытный

il
Offline Offline

« Ответ #4 : 09-04-2014 10:49 » 

Dimka, это ты зря.
Вад точно ответил на мой вопрос. Я так и ожидал, что должно существовать похожее стандартное средство - отображение куска массива на поля объекта. Ответ я получил точно желанный.
Другое дело - а нужно ли это?

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

Поэтому для созданных внешних объектов применим твой 1-й подход, а для работы со свободными участками создавать дополнительные объекты не хочется, тут подходит предложение  Вада.
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 09-04-2014 12:52 » 

ezus, я отнюдь не уверен, что ты вполне понимаешь последствия. Placement new использует кусок памяти по данному ему адресу. Но этот вовсе не значит, что объект внутри устроен исключительно как массив чисел. Та может быть и служебная информация, таблицы виртуальных функций и т.д. Т.е. placement new - это не магическое отображение на поля. И эта халява может дорого стоить.

Пример:
Код: (C++)
#include <algorithm>
#include <iostream>

using namespace std;

class A
{
private:
        int x;
public:
        A() : x(3) {}
};

class B
{
private:
        int x;
public:
        B() : x(5) {}
        virtual void f() {}
};

void print(const int *buf, const int n, const char *msg)
{
        cout << msg << ": ";
        for(int i = 0; i < n; ++i)
        {
                cout << buf[i] << " ";
        }
        cout << endl;
}

int main()
{
        const int n = 10;
        int buf[n];
        fill(buf, buf + n, 0);
        print(buf, n, "Free");
        A *a = new (static_cast<void *>(buf + 1)) A();
        print(buf, n, "A created");
        B *b = new (static_cast<void *>(buf + 3)) B();
        print(buf, n, "A and B created");
        return 0;
}

Free: 0 0 0 0 0 0 0 0 0 0
A created: 0 3 0 0 0 0 0 0 0 0
A and B created: 0 3 0 134515264 5 0 0 0 0 0

Как видишь, объекты A и B содержат лишь одно поле int, в котором должны быть числа 3 и 5. И эти числа появляются в буфере. Но ещё в буфере появляется указатель на виртуальную функцию f объекта B.

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

P.S. Но я вообще полагал, что ты используешь массив для хранения данных, т.е. для работы с этим нужны шаблоны проектирования вроде "посетителя" (visitor) и "приспособленца" (flyweight). Т.е. нужен такой объект, который похож на курсор в базах данных: получает указатель на кусок массива, и дальше знает, какую структуру имеют числа, начинающиеся с этого указателя.
« Последнее редактирование: 09-04-2014 12:58 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #6 : 11-04-2014 18:06 » 

Пример косвенной ссылки через индекс вектора
Код: (C++)
#include <exception>
#include <iostream>
#include <vector>

using namespace std;

template<class Item>
class vecref
{

private:

        vector<Item> &v;
        size_t i;

        Item &r() const
        {
                if(this->i >= this->v.size())
                {
                        throw exception();
                }
                else
                {
                        return this->v[this->i];
                }
        }

public:

        vecref(vector<Item> &v, size_t i) :
                v(v),
                i(i)
        {}

        void operator=(const Item &x) const
        {
                this->r() = x;
        }

        operator Item() const
        {
                return this->r();
        }

        const size_t index() const
        {
                return this->i;
        }

};

int main()
{
        vector<int> a;
        a.push_back(3);
        a.push_back(5);
        a.push_back(7);

        const int i = 1;
        vecref<int> r(a, i);
        cout << r << endl; // 5
        r = 13;
        cout << a[i] << endl; // 13

        return 0;
}
Вектор может менять размеры и его внутренний буфер может пересоздаваться, копироваться без потери ссылки. Если вектор уменьшается до размера, меньше индекса ссылки, при обращении возникает исключительная ситуация. В остальных случаях работает.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
ezus
Опытный

il
Offline Offline

« Ответ #7 : 09-07-2014 09:13 » 

Прошу прощение за позднюю реакцию.
Да, мне пришлось полностью реализовать весь менеджмент памяти. Конечно, это бред с нормальной точки зренияю
Но... Я получил выиграш по скорости от 5 до 30 раз в зависимости от размеров. Чем больше размер, тем больше выигрыш.

В конце концов я отказался от placement new. Но обсуждение было для меня полезным.
Еще раз спасибо.
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines