C/C++

Массивы экземпляров класса

Как в массиве или коллекции хранить экземпляры классов?
Очень часто есть необходимость создания множества экземпляров классов (заранее неизвестно сколько). И для обращения к методам и свойствам любого экземпляра их хранить либо в динамическом массиве либо в коллекции.

Проблема в том, что в C++ нет оператора аналогичного Redim в VB
Если в среде NET будь то VB NET или C++ есть коллекция ArrayList
Пишешь на VB без проблем
Dim coll As New ArrayList, w As Book 'экземпляр класса Book
For i = 0 To n
Dim w As New Служаший(nm(i), i+100)
coll.Add(w)
next i
coll(3).Оклад(); //далее без проблем у любого элемента коллекции вызываешь метод класса
//несм на то что это элем коллекции , он автом преобразуется к типу класса

На С++ подобное у меня вызывает ряд проблем
1)во первых если пишем в NET то, слава богу есть ArrayList (в С++ Builder его нет). Что STL-библиотеку подключать, что-ли?
2)если на NET то код
using namespace System;
using namespace System::Collections; //
…..
book * b; ArrayList * st;
for (int i=0;i<5;i++)
{
b=new book("title",i+100);
st->Add(b);
}
То компилятор выдаёт ошибку
Add' : cannot convert parameter 1 from 'book *' to 'System::Object __gc *'

Если попытаться обойтись вообще без коллекций только динамическим массивом
book * b;
b=new book[5];//ошибка – в классе Book нет конструктора без параметров
//а почему он собственно должен быть?
Возможно выходом было бы использование приёма хранения экземпляров созданных объектов в статическом массиве класса (т.е внутри класса)?

Как вообще на C++ программисты управляются с множкством объектом класса?

Программисты на С++ читают страуструпа сначала, и вникают в концепцию указателей, конструкторов и прочих сложных терминов.

1. .NET не пользуюсь, и хелп лень открывать, но навскидку предположу что там написано что ArrayList предназначен для хранения указателей на базовый System::Object. Хотя по следам прочтенной книжки - вроде бы там все классы наследуются от него? Ну да ладно.

2. Конструктор без параметров, видимо, нужен для того, чтобы инициализировать 5 объектов, которые вы создали вызовом new book[5]. А что, по-вашему, должно быть внутри этих объектов после создания?

Как управляться? Да как хочешь: хочешь - пиши свой велосипед, хочешь - используй STL, или другие расширения. Встроенного в язык динамического массива тут действительно нет. Но все средства разработки с радостью предложат альтернативу - будь то ArrayList, СArray или vector.
grigsoft уже все сказал

Сообщение отредактировал European - 21:03:2007, 14:45
В поставленном мной вопросе я хотел фактически сказать по-моему довольно важную вещь.
Жаль, что такая негативная реакция.
Если
1) разрабатываемый класс на C++ должен иметь много реализаций
2) Некоторые его методы должны возвращать не одно а массив или список значений

То реализация данного класса существенно зависит от среды разработки, будь то древний Borland C++ 3.1 или С++ Builder или MS Visual C++ 6.0 или NET C++
.Т.е невозможно или трудно спроектировать универсальный класс, отвечающий
требованиям 1), 2) который можно использовать в вышеперечисленных средах.
В самом деле требование 1 без контейнеров выполнить сложно. Но контейнеры то разные в средах разработки: если C++ 3.1 то вообще не знаю, можно ли там со STL работать и использовать шаблон vector Если всё же с std::vector т.е конструкция типа vector <MyKlass> ekz
То для класса MyKlass необходим конструктор без аргументов, что ограничивает разработчика.
Аналогичная ситуация с CArray(MyClass,MyClass) (Visual C++) –тоже нужен пустой конструктор
NET теперь предоставила контейнер ArrayList – но те же проблемы
Ладно, проще уступить, сделал таки пустой конструктор. Далее 1 и то же объявление
using namespace std;
std::vector <book> B;
в C++ Builder проходит без проблем, в NET компилятор говорит: error C2039: 'vector' : is not a member of 'std' несмотря на то что в подсказке на std:: есть vector ???
Пришлось на NET обойтись вообще без контейнера, заменив его динамич массивом указателей:
book ** B=new book*[n];
При этом в цикле B[i]=new book(tit,"Ivanov",i+100); - проходит
Но для преобразования консольного ввода к типу char – ничего проще чем
String *tt = Console::ReadLine(); //так как потоков cin>> в NET нет!!
char* tit = (char*)(void*)Marshal::StringToHGlobalAnsi(tit);
(согласитесь – нетривиально – откуда новичку вообще знать про Marshall – на сайтах по программированию типа Intuit.ru, Progs.biz.ru такие «тонкости» не публикуют – спасибо, сын помог)

По поводу требования 2 – конечно методы могут возвращать массив значений по ссылке
Но например при работе в C++ Builder удобен его специфический класс TStrings
Т. Е типа TStrings *p=new TStringList(); p->Text=”Загол1\nЗагол2\nЗагол3\n”
При этом возвращаемое методом значение этого типа одним махом можно отобразить например в ListBox,ComboBox: : ListBox1->Items=p
Но если в классическом C++ то кроме char * ничего нет и при написании консольных приложений чаще всего такие методы должны выводить возвращаемое знач в виде массив(списка) на печать И, согласитесь неприятно при формировании char * вместо быстрого сцепления использ функц strcat а потом еще (если необходимо) разбирать этот выходной char * в реализации для выделения каждого элемента контейнера.

Цитата(Е.Багоцкий @ 23:03:2007, 10:54 )
1)Я только хотел сказать ,что концепция ООП основанная на использовании в новых разработках ранее разработанных классов не совсем гладко проходит если разработанные классы планируется использовать в будущем в разных средах разработки. Возсожно в этом виноваты Borland и Microsoft наплодившие в своих средах только ими применяемые типы и контейнеры по сравнению с классическим ANSI C++
2)Изыиняюсь за оговорку, говоря о "реализации класса" имел в виду не созданный объект класса, а сам процесс проектирования класса.
3)По поводу хранения указателей на класс - принимаю и обдумаю.
4) а как всё таки правильно в NET C++ работать со STL - почему ошибка ?
Так какие проблемы - пишите на стандартном С++, и не будет проблем с совместимостью. Предлагаемые расширения помогают постоянным разработчикам - но если вы пишите с VCL - глупо надеятся на то что этот код заработает на .NET. А если вы решите писать на .NET - не ждите что gcc с радостью его вам скомпилирует. Откуда вообще взялась цель использовать результат в разных средах?
Цитата(Е.Багоцкий @ 23:03:2007, 11:17 )
4) Я работаю на NET 2003 C++ (неуправляемое).Консольный вариант. Начало кода
#include "stdafx.h"
#include "book.h"
#using <mscorlib.dll>
#include <string.h>
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace std;

int _tmain()
{ int n;//кол-во экз класса
std::vector <book> B; //уже здесь компилятор спотыкается
error C2039: 'vector' : is not a member of 'std'
Цитата(Е.Багоцкий @ 23:03:2007, 11:51 )
Так то оно так, только всё равно пишем ли Using namespace std
вызываем ли vector<> или std: vector<> - результат один - не пропускает.
System::Runtime::InteropServices; тут не причём - исключал его вместе с строчками соотв кода - результат 1 и то же


На объявление Book хотелось бы взглянуть.
Цитата(Е.Багоцкий @ 23:03:2007, 12:02 )
Да, спасибо .Именно этого не хватало.
Правильный минимум таков:
#include <vector>
using namespace std;
...
vector <book *> //массив указателей на класс
B; book b;
.....
b= book(tit,"Ivanov",i+100);
B.push_back(&b);
Получается с STL это удаётся.
Для полноты счастья попробую с CArray под Visual C++

vector <book *> B; book b; //извините в предыд ответе случайно произошел перенос
Только что понял, что преждевременно сказал, что успех. Код выше, запихивает в вектор указатели на объект, а не сами объекты, при этом т.к сам объект меняет ссылку - то вектор содержит указатели не на все объекты - а лишь на последний созданный объект.
Правильный код всё таки использует хранение в векторе самих объектов а не указателей
book b;
vector <book > B;

b=book(tit,au,ns,jnr);
B.push_back(b); // а не предыдущий вариант -проверял работает правильно.
Так что тезис о хранении в векторе указателей на объекты а не сами объекты вызывает сомнение

Цитата(Е.Багоцкий @ 23:03:2007, 14:47 )
//C++ Builder
using namespace std;
lib_card c; vector <lib_card *> C;
book b; vector <book *> B;
void __fastcall TForm1::bOKClick(TObject *Sender)
{
AnsiString tit=en->Text; AnsiString au=ea->Text;
int ns=StrToInt(ek->Text);
int jnr= cj->ItemIndex;
b=book(tit,au,ns,jnr);
B.push_back(&b);
pAdd->Hide();
}

TForm1::setBooks(int nj) //отображение книг заданного жанра
{lb->Clear();
for (int i=0;i<B.capacity();i++)
{
if (nj < 0) lb->Items->Add(B[i]->show_bookStr());
else if (B[i].show_janr()==nj) //здесь в цикле по i должны отображ названия разных книг !!!
lb->Items->Add(B[i]->show_bookStr()); //а отображаются – последней созданной книги !!!
}
}

void __fastcall TForm1::cmbJChange(TObject *Sender)
{ int nj=cmbJ->ItemIndex;
setBooks(nj);
}
При изменении всего лишь 3 операторов vector <book *> B; на vector <book > B;
И B.push_back(&b); на B.push_back(b);
И B[i]->show_janr() на B[i].show_janr() получается всё верно - после прохода цикла по
B.capasity() в списке отображаются разные книги, чего нет в предыдущ случае !!!

Потому и говорим в 2 голоса - читать и осмысливать страуструпа. Хотя после VB, верю, непросто
B.push_back(new book(tit,au,ns,jnr));
Код
Да ,я это понял. Спасибо.
Всё-таки интересно работать с классами, когда много экземпляров а не 1 -3 как в учебных примерах
которыми нас пичкают на учебных сайтах.