Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Оператор New и создание массива экземпляров класса

15K
16 июня 2007 года
nevi
35 / / 13.05.2007
Восполните кто-нибудь пятно в моих знаниях:
Как с помощью оператора new создать массив классов с параметрами в конструкторе

т.е. есть клас cl_1, и его конструктор cl_1(int), и есть указатель на начало массива: cl_1 *mas;

тогда например создание одного экземпляра класса: mas = new cl_1(A);, где А соответственнон - параметр для конструктора. А как мне создать и инициализировать таким образом массив из N экземпляров класса???
394
16 июня 2007 года
MegaMozg
317 / / 18.03.2006
Используй конструктор с параметром по умолчанию, или конструктор, считывающий значения глобальных переменных.
242
17 июня 2007 года
Оlga
2.2K / / 04.02.2006
Можешь также сохранять твой массив классов при помощи контейнера ArrayList. При создании массива данного типа (ArrayList) тебе не надо будет указывать параметры, но при добавлении экземпляра класса cl_1 в массив.
15K
17 июня 2007 года
nevi
35 / / 13.05.2007
Цитата: MegaMozg
Используй конструктор с параметром по умолчанию, или конструктор, считывающий значения глобальных переменных.


ну это-то не проблема... эх.
ArrayList - это билдерская штука? не люблю их использовать. Лучше когда все тобою писано )

590
17 июня 2007 года
Gigahard
223 / / 03.04.2006
Тады используй vector из STL...
Код:
#include <vector>
using namespace std;
...

class Test
{
    public:
    Test(){}
    Test(int a):myData(a){}

    private:
    int myData;
};
vector<Test>* mas; //Объявляем указатель на динамический массив объектов Test в динамической памяти;

mas= new vector <Test> (10, 25); //Инициализируем динамический массив из 10 объектов Test с передаваемым параметром "25".
mas->push_back(Test(44)); //Добавляем в конец массива 11й элемент типа Test инициализированный значением 44
(*mas)[10]=21; //Обращаемся к элементу массива по индексу, изменяем значение 11го элемента на 21... Применяем скобки() для установки приоретета оператора разыменования.
mas->pop_back();//Удаляем последний(11й) элемент массива
// ... какой то код

delete mas; //Освобождаем выделенную память


Vector - это часть C++. Конечно несколько навороченней чем стандартный статический массив, но и удобней.
15K
17 июня 2007 года
nevi
35 / / 13.05.2007
ой как все сложно =() мне тогда проще будет добавить функцию Init с параметрами для инициализации и вызывать в цикле.

Я просто думал, что у оператора new со всеми его перегрузками безграничные возможности.
590
17 июня 2007 года
Gigahard
223 / / 03.04.2006
Да ничего сложного... Комментов больше, чем программного кода. Одно "лишнее" ключевое слово и два вида скобочек...
В остальном с вектором можно работать как с обычным массивом через индексы...
 
Код:
vector <Class_name>* mas= new vector <Class_name> (vectorSize, Class_InitValue);
(*mas)[10]=21;
delete mas;

Плюс получаете гибкость с добавлением новых элементов в любой момент выполнения программы.
255
18 июня 2007 года
Dart Bobr
1.4K / / 09.04.2004
Цитата: nevi
ой как все сложно =() мне тогда проще будет добавить функцию Init с параметрами для инициализации и вызывать в цикле.

Я просто думал, что у оператора new со всеми его перегрузками безграничные возможности.


Ага, может тебе еще проще будет вместо деструктора написать процедуру?? Это называется извращаться через ж***. Конструкторы для того и предусмотрены - чтобы создать и инициализировать элементы обьекта, и ничего в них сложного нет.

15K
18 июня 2007 года
nevi
35 / / 13.05.2007
2ДартБобер: я в курсе - зачем нужны конструкторы, но я не люблю использовать лишние навороты, тем более когда задача не обязывает.
2Гигахард: спасибо, думаю остановлюсь всеже на этом способе.
63
18 июня 2007 года
Zorkus
2.6K / / 04.11.2006
Цитата: nevi
ну это-то не проблема... эх.
ArrayList - это билдерская штука?


Нет, это дотнетовская.

Цитата: nevi

не люблю их использовать. Лучше когда все тобою писано )


И очень зря. Сначала, конечно, нужно разобраться, что такое массив, что такое динамический массив и.т.д. Но затем практически всегда гораздо лучше использовать готовые, выверенные, мощные и отлично документированные решения, чем изобретать велосипед. Кодя то, что давно уже было кем то сделано до тебя, ты просто теряешь время, которое мог бы употребить на проработку логики, например.
[QUOTE=Gigahard]
Vector - это часть C++. Конечно несколько навороченней чем стандартный статический массив, но и удобней.[/QUOTE]
Звучит так, словно ты извиняешься за навороченность вектора. Нечто вроде "Не слишком хорошее решение, но все же получше чем твое". Почему?

Цитата: nevi

ой как все сложно =() мне тогда проще будет добавить функцию Init с параметрами для инициализации и вызывать в цикле.


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

Цитата: nevi

Я просто думал, что у оператора new со всеми его перегрузками безграничные возможности.


Возможностей много. Но вовсе не безграничны они. И кстати, тот самый вектор использует в своей внутренней реализации этот оператор для выделения памяти под элементы. Только незаметно для пользователя вектора.
[QUOTE=Gigahard]
Да ничего сложного... Комментов больше, чем программного кода. Одно "лишнее" ключевое слово и два вида скобочек...
В остальном с вектором можно работать как с обычным массивом через индексы...
Плюс получаете гибкость с добавлением новых элементов в любой момент выполнения программы.[/QUOTE]
Без обид, но ощущение такое, как будто ты ловкий торговец и пытаешься всучить вектор автору темы, всячески рекламируя его полезность:D.

350
18 июня 2007 года
cheburator
589 / / 01.06.2006
Цитата: Gigahard

mas= new vector <Test> (10, 25); //Инициализируем динамический массив из 10 объектов Test с передаваемым параметром "25".


У вектора нет конструктора, который в качестве параметра берет значение, не являющееся объектом того класса, какого есть вектор, т. е. если имеется vector<Test>, мы в конструктор вектора первым параметром передаем число элементов, вторым - объект типа Test.
При этом сам вектор, создавая объекты Test, вызывает их копирующий конструктор.
Приведенный вами код компилируется, более того, работает правильно в MS VC 2005, хотя не должен. Не буду рассказывать о результатах отладки этого кода, но похоже, тут глюк компилятора.
В приведенном коде по логике вещей должен вызываться конструктор
template<class _Iter>
vector(_Iter _First, _Iter _Last),
т. е. 10 и 25 должны рассматриваться как итераторы.

Выхода из ситуации два:
1. vector <Test> (10, Test(25))
2. Используем вызов оператора new таким образом, чтобы не вызывались конструкторы объектов. Потом отдельно для каждого объекта вызываем конструктор. Тут возможна передача произвольного числа произвольных параметров:

Код:
class Test
{
public:
    Test(int a, double b):myData(a), data2(b) {};
private:
    int myData;
    double data2;
};
Test *arr = reinterpret_cast<Test*> (::operator new[] (sizeof (Test) * 10));
Test *ptr = arr;
for (size_t i = 0; i < 10; ++i, ++ptr)
    ::new (ptr) Test (12, 55.77);

3. Если все-таки нужен вектор, то вот так:
 
Код:
vector<Test> vec;

vec.reserve(10);
for (size_t i = 0; i < 10; ++i)
  vec.push_back(Test(12, 55.77));

Но здесь происходит инициализация локального объекта Test, затем его копирование в вектор, т. е. лишние затраты времени на копирование. Более того, если объект имеет семантику Noncopyable, такой код не проканает.
590
19 июня 2007 года
Gigahard
223 / / 03.04.2006
Этот код работает, потому что происходит следующее...

В C++ запись Test x=25 равнозначна записи Test x= Test(25).

Для этого в классе должен быть определен перегруженный конструктор, который принимает один параметр присваиваемого типа.
При этом происходит неявная инициализация передаваемого значения в объект Test. Данное правило справедливо только для конструкторов с одним передаваемым параметром.

Т.е. запись vector <Test> (10,25) равнозначна коду vector <Test> (10, Test(25)).

Оба варианта равноправны и не являются ошибочными.
15K
19 июня 2007 года
nevi
35 / / 13.05.2007
т.е. вот так сразу задать для конструктора больее одного параметра нельзя?? нужно конструировать создавать отдельно и передавать указатель во втором параметре?
350
19 июня 2007 года
cheburator
589 / / 01.06.2006
Цитата: Gigahard
Этот код работает, потому что происходит следующее...

В C++ запись Test x=25 равнозначна записи Test x= Test(25).

Для этого в классе должен быть определен перегруженный конструктор, который принимает один параметр присваиваемого типа.
При этом происходит неявная инициализация передаваемого значения в объект Test. Данное правило справедливо только для конструкторов с одним передаваемым параметром.

Т.е. запись vector <Test> (10,25) равнозначна коду vector <Test> (10, Test(25)).

Оба варианта равноправны и не являются ошибочными.



Однако, отладка под MS VC++ 2005 показала, что это не так. Для кода
vector<Test> vec (10, 25)
вызывается конструктор (как я писал выше)
template<class Iter>
vector(Iter first, Iter last)
Но каким-то чудом этот конструктор приводит к правильной работе :)
А записи
Test x = 25
и
Test x = Test(25)
неравнозначны.
Во-первых, Test x = 25 равнозначна записи Test x (25) и требует наличия конструктора с параметром типа int или иного целого типа. Он и вызывается.
Во-вторых, запись Test x = Test (25) равнозначна записи
Test x (Test(25))
и здесь нужны два конструктора - описанный выше и копирующий конструктор. Сработают оба. Создается временный безымянный объект Test (25), который передается параметром в копи-конструктор и тут же уничтожается.
А в записи
vector<Test> vec (10, 25)
второй параметр в любом случае не воспринимается компилятором как значение типа Test, тут нужно явное преобразование, о котором я писал:
vector<Test> vec (10, Test(25)).
Это, конечно, небольшой оффтоп, но надо проверить на других компиляторах, чтобы выяснить, кто прав :)

350
19 июня 2007 года
cheburator
589 / / 01.06.2006
Цитата: nevi
т.е. вот так сразу задать для конструктора больее одного параметра нельзя?? нужно конструировать создавать отдельно и передавать указатель во втором параметре?



Сразу - нет, не получится, массивы инициализируются только конструктором по умолчанию.
"Нормальный", рабочий код приведен выше.
К тому же подобный код используется (как правило, но не обязательно всегда) классом vector. Т. е. сначала выделяется память под нужное число элементов, потом каждый отдельно инициализируется.
Примерно такова же и внутренняя реализация оператора new[] (опять же не обязательно всегда), который выделяет массивы.
Так что по затратам времени приведенный код ничем не уступает ни вектору (где вызывается копи-конструктор с одним параметром), ни объявлению массива (где вызывается конструктор по умолчанию), а имеет только преимущество - произвольное число произвольных параметров. Так что Ctrl+C - Ctrl+V и пошел :)

15K
19 июня 2007 года
nevi
35 / / 13.05.2007
2cheburator:простите мне мое невежество, но в строчке
" Test(int a, double b):myData(a), data2(b) {};" что означает все что после двоеточия? (честно говоря остальные несколько строчек тоже не совсем понятны) В общем - спасибо, буду разбираться.
350
20 июня 2007 года
cheburator
589 / / 01.06.2006
Цитата: nevi
2cheburator:простите мне мое невежество, но в строчке
" Test(int a, double b):myData(a), data2(b) {};" что означает все что после двоеточия? (честно говоря остальные несколько строчек тоже не совсем понятны) В общем - спасибо, буду разбираться.


Поясняю правила инициализации подобъектов в конструкторе.
После двоеточия идет инициализация базовых классов и подобъектов, т. е. членов класса. В данном случае имеется два подобъекта: int myData и
double data2. Они инициализируются значениями, которые переданы конструктору в параметрах (т. е. a и b).
Если бы класс был унаследован от другого, то сначала идет инициализация базовых классов, затем уж подобъектов. Например

Код:
class Base
{
public:
  Base (int _x)
  :x (_x)
  {
  };
private:
  int x;
};

class Derived : public Base  // Наследуем этот класс от Base
{
public:
  Derived (int _x, double _y)
  :Base (_x), y (_y)
  {
  };
private:
  double y;
};

Не буду вдаваться в подробности, но инициализацию подобъектов очень желательно производить именно таким образом, а не таким, например:
Код:
class Derived : public Base  // Наследуем этот класс от Base
{
public:
  Derived (int _x, double _y)
  :Base (_x)
  {
    y = _y;
  };
private:
  double y;
};

Более сложный код:
 
Код:
Test *arr = reinterpret_cast<Test*> (::operator new[] (sizeof (Test) * 10));
Test *ptr = arr;
for (size_t i = 0; i < 10; ++i, ++ptr)
  ::new (ptr) Test (12, 55.77);

Поясняю.
Имеется оператор new (и new[] для массивов).
Если вызывать его в обычной форме, как например
Test *arr = new Test [10];
компилятор генерирует код, который вызывает непосредственно сам оператор new (который занимается выделением памяти), плюс код для вызова конструкторов.
А вот если написать
operator new[],
то вызывается только непосредственно оператор new, без вызова конструкторов (т. е. происходит чисто выделение памяти).
Идем дальше.
Есть специальная форма оператора new, называемая placement operator new, которая просто ничего не делает. Эта форма рассчитана на то, что память уже выделена. Этот оператор в качестве параметра принимает указатель на память, которая была уже выделена.
Тогда код
new (Указатель) Тип (параметры)
будет компилироваться так. Как я и указывал выше, будет сгенерирован код, вызывающий непосредственно оператор (который ничего не делает), затем - вызов конструктора. По сути, такая форма оператора new - единственный способ "явно" вызвать конструктор объекта.
Итак:
 
Код:
Test *arr = reinterpret_cast<Test*> (::operator new[] (sizeof (Test) * 10));  // Выделение памяти под 10 объектов Test
Test *ptr = arr;  // ptr - указатель на текущий элемент массива (двигается в цикле)
for (size_t i = 0; i < 10; ++i, ++ptr)
  ::new (ptr) Test (12, 55.77);  // Вызов конструктора для очередного элемента
3
20 июня 2007 года
Green
4.8K / / 20.01.2000
Цитата: cheburator

Не буду вдаваться в подробности, но инициализацию подобъектов очень желательно производить именно таким образом


Ну а если вдаваться в подробности? :)

Цитата: cheburator

Более сложный код:
 
Код:
Test *arr = reinterpret_cast<Test*> (::operator new[] (sizeof (Test) * 10));
Test *ptr = arr;
for (size_t i = 0; i < 10; ++i, ++ptr)
  ::new (ptr) Test (12, 55.77);


Да, действительно сложный.
Почему бы не сделать так?

 
Код:
Test *arr = reinterpret_cast<Test*>new unsigned char[sizeof (Test) * 10];

for (size_t i = 0; i < 10; ++i)
  ::new (&arr) Test (12, 55.77);
350
20 июня 2007 года
cheburator
589 / / 01.06.2006
Цитата: Green

Ну а если вдаваться в подробности? :)


Подробно: для подобъекта вызывается конструктор по умолчанию, а затем оператор присваивания (для вышеприведенного примера). Что зачастую излишне.
А если конструктора по умолчанию нет? А если оператора присваивания нет?

Цитата: Green

Почему бы не сделать так?
 
Код:
Test *arr = reinterpret_cast<Test*>new unsigned char[sizeof (Test) * 10];


А new unsigned char [...] ... не вызовет ли конструирование массива объектов типа char значениями по умолчанию? Что опять же излишне? Тем более, если речь идет не о встроенных типах?

Цитата: Green

Почему бы не сделать так?
 
Код:
for (size_t i = 0; i < 10; ++i)
  ::new (&arr) Test (12, 55.77);


Пардон, привычка, появившаяся недавно.
В данный момент времени речь идет об обычном массиве, и приведенный Вами код вполне хорош.
Но если речь идет о таких вещах, как vector, может получиться так, что для каждой итерации цикла вызывается оператор [], что может занять большее время, чем ++ptr. В то же время мы знаем, что данные в vector располагаются "рядом", и ++ptr приведет нас гарантированно к следующему элементу вектора, чем мы и воспользовались.

590
20 июня 2007 года
Gigahard
223 / / 03.04.2006
Цитата: cheburator
Однако, отладка под MS VC++ 2005 показала, что это не так. Для кода
vector<Test> vec (10, 25)
вызывается конструктор (как я писал выше)
template<class Iter>
vector(Iter first, Iter last)
Но каким-то чудом этот конструктор приводит к правильной работе :)
А записи
Test x = 25
и
Test x = Test(25)
неравнозначны.
Во-первых, Test x = 25 равнозначна записи Test x (25) и требует наличия конструктора с параметром типа int или иного целого типа. Он и вызывается.
Во-вторых, запись Test x = Test (25) равнозначна записи
Test x (Test(25))
и здесь нужны два конструктора - описанный выше и копирующий конструктор. Сработают оба. Создается временный безымянный объект Test (25), который передается параметром в копи-конструктор и тут же уничтожается.
А в записи
vector<Test> vec (10, 25)
второй параметр в любом случае не воспринимается компилятором как значение типа Test, тут нужно явное преобразование, о котором я писал:
vector<Test> vec (10, Test(25)).
Это, конечно, небольшой оффтоп, но надо проверить на других компиляторах, чтобы выяснить, кто прав :)



Читаем справочник Шилдта по С++, описывающий ANSI стандарт, Раздел "Конструкторы с параметрами:особый случай"

Запись Test x=25 является эквивалентом записи Test x= Test(25) и для компилятора они равнозначны!

Под билдером шестым тоже никаких глюков не замечено.

ИМХО в случае с классами, вектор не занимается проверкой типов, возлагая эту ответственность на конструкторы самого класса, а они в свою очередь допускают такой формат записи.

350
20 июня 2007 года
cheburator
589 / / 01.06.2006
Цитата: Gigahard
Запись Test x=25 является эквивалентом записи Test x= Test(25) и для компилятора они равнозначны!



Да, действительно, признаю ошибку.
Но все-таки я прав насчет вызова конструктора, поскольку первый конструктор больше подходит, т. к. в нем нет неявного преобразования типов.
А вот запись
vector<Test> vec (10, Test(25))
гарантированно даст нужный результат.

3
21 июня 2007 года
Green
4.8K / / 20.01.2000
Цитата: cheburator
Подробно: для подобъекта вызывается конструктор по умолчанию, а затем оператор присваивания (для вышеприведенного примера). Что зачастую излишне.
А если конструктора по умолчанию нет? А если оператора присваивания нет?


Правильный ответ.

Цитата: cheburator

А new unsigned char [...] ... не вызовет ли конструирование массива объектов типа char значениями по умолчанию? Что опять же излишне?


Не вызовет.

Цитата: cheburator

Тем более, если речь идет не о встроенных типах?


Речь идет о конкретном типе unsigned char[], ну можно и char[]. И используется он для выделения памяти. Ты видимо не понял примера.

Цитата: cheburator

Пардон, привычка, появившаяся недавно.
В данный момент времени речь идет об обычном массиве, и приведенный Вами код вполне хорош.
Но если речь идет о таких вещах, как vector, может получиться так, что для каждой итерации цикла вызывается оператор [], что может занять большее время, чем ++ptr. В то же время мы знаем, что данные в vector располагаются "рядом", и ++ptr приведет нас гарантированно к следующему элементу вектора, чем мы и воспользовались.


Ещё раз. Не разрывай пример. Речь идет о типе unsigned char[]. Здесь vector ни с какого боку.

P.S. Кстати, про скорость... ты имеешь неверное представление об оптимальности. Скорость работы не должна быть помехой в разрабртке, а преждевременная оптимизация опасна.

Знаете кого-то, кто может ответить? Поделитесь с ним ссылкой.

Ваш ответ

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог