Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Инициализация константного динамического массива


Автор: JackYF 6.4.2007, 23:56
Возможно, данная тема уже пробегала на форуме. Однако, в поиске не нашел.

Итак, проблема следующая. Есть некий класс, у которого есть константное поле.
Раз поле константное, то оно должно ициализироваться в конструкторе.

Но мне нужно создать (динамически) массив элементов данного класса, причем каждому элементу нужно задать свои параметры конструктора.

Какие есть варианты, кроме malloc + placement new (и связанных потом с этим извращений)?

Автор: vinter 7.4.2007, 00:01
остается еще явная инициализация, по другому вроде никак.

Автор: threef 7.4.2007, 00:20
Синтаксически создать массив констант нельзя. Предлагались статические варианты или,ИМХО  лучший - создать структуру, содержащую массив. Ее-то и можно проинициализировать в конструкторе.
Код


struct point
{
   int x[2];
} x={1,2};

struct classik{
 const  point ;
  classik():point(x){}
};

 

Автор: Xenon 7.4.2007, 01:28
... Недопонял smile

Автор: JackYF 7.4.2007, 11:49
threef, через список инициализаторов не пойдет. Мне нужен полноценный вызов конструктора с несколькими параметрами для каждого элемента....

Автор: Xenon 7.4.2007, 12:42
Может так?
Код

template <class T>
class Foo
{
public:
    const T* const pTr;
    Foo(T arr[],int size):pTr(new T[size])
    {
        for (int i = 0; i < size; ++i)
        {
            static_cast<T>(pTr[i]) = arr[i];
        }
    }
    ~Foo()
    {
        delete [] pTr;
    }
};

Автор: JackYF 7.4.2007, 13:58
Xenon, честно - не понял, что ты этим хотел сказать...

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

Код

class Foo {...};

unsigned long size = 255;
void* storage = malloc (size * sizeof(Foo));
Foo* p = (Foo*)storage;
for (int i = 0; i < size; ++i )
{
  p = new(p) Foo(i, i*i + 1);
  ++p;
}
...
// working with array
p = (Foo*)storage;
for (int i = 0; i < size; ++i )
{
  //p->~Foo();
  // или
  //delete p;
  ++p;
}

//free(storage) - если через деструктор...

Автор: Daevaorn 7.4.2007, 14:48
Цитата(JackYF @  7.4.2007,  14:58 Найти цитируемый пост)
Xenon, честно - не понял, что ты этим хотел сказать...

Он тебе показал решение

Автор: Xenon 7.4.2007, 16:22
JackYF, Я просто не очень понял что ты хотел. Исходя из заголовка я понял, что тебе нужен в классе константный динамический массив, который ты сможешь иницилизировать значениями, переданными в конструктор. Вот я тебе исходя из этих данных накидал пример.
Какая у тебя задача-то стоит? Зачем ЭТО?

Автор: likehood 7.4.2007, 16:35
JackYF, может http://www.lib.ru/CTOTOR/cpp3comm.txt#297 подойдет.

Автор: likehood 7.4.2007, 17:04
можно еще вместо массива использовать вектор:
Код

vector<Foo> v;
for (int i=0; i<size; i++)
  v.push_back(Foo(i, i*i+1));

Автор: JackYF 7.4.2007, 23:35
Цитата(likehood @  7.4.2007,  17:04 Найти цитируемый пост)
можно еще вместо массива использовать вектор:


по-моему, тоже не пойдет.
Вектор для присваивания будет использовать стандартную семантику присваивания, то есть operator=. Что в данное случае опять-таки недопустимо, присваивание константным элементам класса можно только в конструкторе.

Цитата(Xenon @  7.4.2007,  16:22 Найти цитируемый пост)
Какая у тебя задача-то стоит?

Вообще говоря, такая задача возникает уже второй раз... Зачем было первый, не помню.
А вот вкратце кусок задачи - постараюсь объяснить...

Есть класс. В котором содержится массив объектов. Массив public. Каждый объект содержит содержит несколько полей, из которых пользователь может все читать, но только часть писать. Те 2 поля, которые константные, инициализируются один раз при создании и больше не изменяются.

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

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

Автор: JackYF 7.4.2007, 23:53
Цитата(likehood @  7.4.2007,  16:35 Найти цитируемый пост)
JackYF, может это подойдет. 


Нет, не пойдет... опять все сводится к создание уже созданного массива значений и присваивания.
Ни массива у меня нету и быть не может (а зачем мне тогда делать еще один? я лучше указатель создам),
ни присваивания... (выше написал, почему).

Автор: likehood 8.4.2007, 10:16
Цитата(JackYF @  8.4.2007,  00:53 Найти цитируемый пост)
Нет, не пойдет... опять все сводится к создание уже созданного массива значений и присваивания.

Лучше покажу работающий код, а уж ты решай - пойдет он тебе или нет.
Код

#include <iostream>
struct Foo {
    const int i;
    Foo(int i) : i(i) { }
};
int main()
{
    static int curr = 0;
    struct Foo_local : public Foo {
        Foo_local() : Foo(curr*curr) { ++curr; }
    };
    Foo *foo = new Foo_local[10];
    std::cout << foo[5].i << "\n";
    delete[] foo;
    return 0;
}

Автор: Earnest 9.4.2007, 20:36
Цитата(JackYF @  8.4.2007,  00:35 Найти цитируемый пост)
Сделать эти поля не константыми, а приватными у класса, фукнцию их чтения public, функцию их записи в приват и френдом надкласс. Да, это выход. Но это менее красиво...

Срочно меняй свои представления о красоте!  smile public - данные - вот это некрасиво. Особенно в перспективе дальнейших изменений кода.

Цитата(JackYF @  8.4.2007,  00:35 Найти цитируемый пост)
Да ну и вообще интересно с той точки зрения, что вообще с точки зрения программиста желания сделать массив с описанными мной свойствами возможно; и для компилятора, в общем-то, не проблема... Но вот не получается. 

Один (уж не помню кто) из столпов программирования писал, что любую проблемму в программировании можно решить добавлением нужного числа прокладок в нужные места. Цитирую не дословно, но смысл тот.
Вот и здесь то же самое, treef уже предлагал: дополнительная структура, содержащая твой массив. массив в структуре не константный, поэтому инициализируй его как хочешь. А саму структуру в объемлющем классе сделай константной. Точно, была уже такая тема, и там это предлагали, может даже я...

Но лучше так не делай, а все спрячь подальше. Public-данные - это ЗЛО!

Автор: JackYF 10.4.2007, 00:39
likehood, мне надо будет отдавать пользователю ссылки на элементы массива. В данном случае тип элементов foo_local, о котором пользовает не знает и не узнает, так как он локальный.


Цитата(Earnest @  9.4.2007,  20:36 Найти цитируемый пост)
public - данные - вот это некрасиво.

Ну, не всегда. Особенно если они константные...

Цитата(Earnest @  9.4.2007,  20:36 Найти цитируемый пост)
поэтому инициализируй его как хочешь.

чем? уже созданным внешне массивом?

Цитата(Earnest @  9.4.2007,  20:36 Найти цитируемый пост)
инициализируй его как хочешь.

Как хочу? Дык вот я и хочу! Вызвать разные конструкторы для разных элементов массива. Но как? Проблема - инициализация массива!

Цитата(Earnest @  9.4.2007,  20:36 Найти цитируемый пост)
А саму структуру в объемлющем классе сделай константной. 

Ы, тоже не мой выход. Некоторые данные должны остаться public.

Earnest, вариант threef'a не подходит. Он предлагает инициализировать надструктуру. Опять-таки - чем? У меня не POD-данные, чтобы их в инициализаторе прописывать между фигурными скобками... Задача - вызов конструкторов. И без локальных классов, мне потом надо будет эти ссылки на эти данные выводить наружу.

Подводя по крайней мере промежуточный итог, резюмирую... не буду предоставлять public-const данные, сделаю через функции.
Но вот абстрактный вопрос инициализации массива до сих пор не решен. Скорее всего, все-таки нельзя. А жалко.

Автор: Earnest 10.4.2007, 07:49
Если массив не константный, то никто не мешает писать в конструкторе структуры:
a[0] = XXX;
a[1] = YYY;
и т.д.
Нужно только позаботиться, чтобы элементы массива имели default-конструкторы.
Что ты передашь в надструктуру для инициализации - твоя проблема, хоть из файла данные читай.
Ты писал изначально о невозможности вызвать конструкторы для каждого элемента массива. Так вот, эта проблема решается в рамках описанного подхода.
А как ты разом передашь для разных элементов инициализирующие данные - совсем другая проблема.

Автор: JackYF 10.4.2007, 14:14
Цитата(Earnest @  10.4.2007,  07:49 Найти цитируемый пост)
a[0] = XXX;
a[1] = YYY;


Operator= не подходит... Уже говорил же ж. Если делать константными данными.

Решение с помощью закрытия данных и работы через функции принято. Вопрос, как вызвать конструкторы элементов массива. Без использования операторов=.

Просто абстрагируясь от моей проблемы: как вызвать <u>конструкторы</u> элементов массива. Не присваивания потом или через внешние структуры.
Сам факт. Нет - значит нет, дело же такое smile

Автор: likehood 10.4.2007, 14:33
Цитата(JackYF @  10.4.2007,  01:39 Найти цитируемый пост)
likehood, мне надо будет отдавать пользователю ссылки на элементы массива. В данном случае тип элементов foo_local, о котором пользовает не знает и не узнает, так как он локальный.

Вообще-то, в моем примере тип массива Foo*, так что никаких проблем. Приведение к базовому классу еще никто не отменял.

Автор: JackYF 10.4.2007, 14:38
Цитата(likehood @  10.4.2007,  14:33 Найти цитируемый пост)
Вообще-то, в моем примере тип массива Foo*, так что никаких проблем. Приведение к базовому классу еще никто не отменял. 


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

Автор: SerpentVV 10.4.2007, 14:53
Поля-массивы в классе

До сих пор в качестве полей мы использовали только скалярные переменные. Но в С++ нет запрета объявлять в классе поле-массив. Естественно, размер класса с полем-массивом увеличивается на размер массива. Однако использование массива в классе имеет некоторые нюансы, связанные с инициализацией . Начнем разбираться с этим вопросом на самом простом примере (листинг 2.23), в котором объявляются и инициализируются несколько полей-массивов.

Код

Листинг 2.23. Поля-массивы в классе
class Arrays
{    int m0[10];
    static const unsigned int k = 10;
    enum { n = 10 };
    int m1[k];
    int m2[n];
    public: 
        Arrays() 
        { for (int i = 0; i < k; ++i) m0[i]=m1[i]=m2[i]=0; }
};   

В классе Arrays объявлено три поля-массива: m0, m1 и m2. Как видим, задать количество элементов массива можно либо явной константой, либо константным выражением со статическими и/или нумерованными константами, причем константы должны быть определены раньше массива. Задавать количество элементов поля-массива обязательно.
Конструктор без аргументов обнуляет массивы, выполняя цикл в теле. Такой способ инициализации поля-массива — в теле конструктора — является наиболее простым. Это позволяет присвоить элементам массива любые значения. Однако наиболее часто выполняется обнуление, поэтому для массивов разрешается применять инициализацию нулем. Тогда наш конструктор выглядит значительно проще:
Код

Arrays(): m0(), m1(), m2() {} 

Для массива объектов некоторого типа такая запись означает вызов конструктора по умолчанию (без аргументов), и не забудьте, что этот вызов выполняется для каждого элемента массива.
К сожалению, применение списков инициализации для полей-массивов этим и ограничивается — в скобках нельзя ничего указывать. Явная инициализация не проходит даже для символьных массивов. Например, если мы объявим в классе Arrays символьный массив
Код

char s[4];

и попытаемся в списке инициализации конструктора присвоить этому массиву символьную константу,
Код

Arrays(): m0(), m1(), m2(), s("abc") {}

то в системе Visual C++.NET 2003 получим сообщение об ошибке:
error C2536: 'Arrays::Arrays::s' :
cannot specify explicit initializer for arrays
нельзя определять явный инициализатор для массивов

Однако мы можем прописать в классе поле-указатель на символ
Код

char *s;

и инициализировать его символьной константой в списке инициализации конструктора, как показано выше.
Как это ни странно, но большие проблемы возникают при попытках объявить в классе константный массив встроенного типа! Как мы выяснили выше, константы не могут быть проинициализированы в теле конструктора и инициализируются только в списке инициализации конструктора. Однако для константного массива встроенного типа не работает даже инициализация нулем.
ПРИМЕЧАНИЕ
Этот вопрос практически не отражен в стандарте, поэтому компиляторы ведут себя по-разному. В системе Visual C++.NET 2003 выдается ошибка компиляции C2439, а Borland C++ Builder 6 выдает только предупреждение W8038 о том, что массив не инициализируется.

Не проходит и отмена константности. Например, мы пропишем массив m0 как константный, а в теле конструктора пропишем инициализацию в цикле
Код

for (int i = 0; i < 10; ++i) 
    const_cast<int>(m0[i]) = 0;

И система Visual C++.NET 2003, и Borland C++ Builder 6 откажутся компилировать такой цикл.
Удивительно, но для константного массива объектов не встроенного типа задавать инициализацию нулем разрешается. Для этого в классе должен быть определен конструктор без аргументов, который вызывается для инициализации каждого элемента константного поля-массива. Например, вполне можно инициализировать константный массив строк,
Код

const string ss[10];

прописав в списке инициализации конструктора инициализацию «нулем» ss(). Как реально инициализируется такой массив, конечно, зависит от реализации конструктора по умолчанию.

Автор: JackYF 10.4.2007, 15:01
SerpentVV, это все было к чему?

Автор: likehood 10.4.2007, 15:04
Цитата(JackYF @  10.4.2007,  15:38 Найти цитируемый пост)
Мне надо это запихнуть в массив без operator=.

Может я плохо понял задачу, но в моем примере operator= не используется, присваивание идет через список инициализации.

Автор: JackYF 10.4.2007, 16:12
Цитата(likehood @  10.4.2007,  15:04 Найти цитируемый пост)
Может я плохо понял задачу

Задача - вызов конструкторов для каждого элемента массива. Список инициализации работает только для POD-типов, тем более значения, передаваемые в конструктор, мне не известны в начале программы.

Автор: likehood 10.4.2007, 16:15
Ну так в моем примере передаваемые значения и вычисляются во время работы программы. Кстати, почему это список инициализации работает только для POD-типов?

Добавлено через 2 минуты и 53 секунды
Может мы говорим о разных вещах? smile
Я говорю об http://forum.vingrad.ru/index.php?showtopic=145000&view=findpost&p=1091495 посте.

Автор: JackYF 10.4.2007, 17:41
Цитата(likehood @  10.4.2007,  16:15 Найти цитируемый пост)
Кстати, почему это список инициализации работает только для POD-типов?

Хм... приведи контрпример.

Цитата(likehood @  10.4.2007,  16:15 Найти цитируемый пост)
Ну так в моем примере передаваемые значения и вычисляются во время работы программы.

да... недосмотрел я тогда, видать. Возможно, это выход. Попробую применить. Спасибо.

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

Автор: likehood 10.4.2007, 18:23
Цитата(JackYF @  10.4.2007,  18:41 Найти цитируемый пост)
Возможно, это выход.

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

Цитата(JackYF @  10.4.2007,  18:41 Найти цитируемый пост)
Хм... приведи контрпример.

Видимо, я тебя неправильно понял. Ты, наверное, под списком инициализации имел ввиду пример threef'а, а я подумал о другом. В таком случае согласен насчет POD-типов.

Цитата(JackYF @  10.4.2007,  18:41 Найти цитируемый пост)
там где раньше в постах переборщил, извините

Разве ж это переборщил. Тут такие перлы иногда выдают...  smile 

Автор: SerpentVV 11.4.2007, 12:10
Цитата(JackYF @ 10.4.2007,  15:01)
SerpentVV, это все было к чему?

Это к тому, что в списке инициализации вызывается конструктор по умолчанию - для неPOD-типов.

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

В третьих, несколько противоречивая постановка: инициализировать константный массив значениями, которые не известны в начале работы программы...
Тогда это - не константный массив, если значения констант неизвестны при компиляции...

Если есть желание просто защитить от изменений - нет нужны объявлять массив константным... Можно решить этот вопрос на уровне прав доступа...
 

Автор: JackYF 11.4.2007, 15:01
Цитата(SerpentVV @  11.4.2007,  12:10 Найти цитируемый пост)
инициализировать константный массив значениями, которые не известны в начале работы программы...


Никаких противоречий...

Код

const std::time_t beg_time = std::time();


Такой код тебя не смущает? А ведь значение явно не будет известно на этапе компиляции...
Так и у меня. У меня некоторые поля класса константные... Которые, естественно, инициализируются в конструкторе. Не по-умолчанию...

А в принципе, как минимум два пути решения вопроса уже озвучены.
1) с помощью прав доступа
2) с помощью локальных классов

Всем спасибо, тему можно закрывать.

Автор: SerpentVV 11.4.2007, 15:27
Ага, понял... Логическая константность, естественно, не всегда равна физической... smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)