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

  • Автор темы Автор темы eugira
  • Дата начала Дата начала
E

eugira

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

Проблема в том, что в 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.
 
В поставленном мной вопросе я хотел фактически сказать по-моему довольно важную вещь.
Жаль, что такая негативная реакция.
Если
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=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 * в реализации для выделения каждого элемента контейнера.
 
<!--QuoteBegin-Е.Багоцкий+23:03:2007, 10:54 -->
<span class="vbquote">(Е.Багоцкий @ 23:03:2007, 10:54 )</span><!--QuoteEBegin-->То реализация данного класса существенно зависит от среды разработки
[snapback]59918" rel="nofollow" target="_blank[/snapback]​
[/quote]
Реализация никак не может зависеть от среды, она зависит только от версии компилятора
<!--QuoteBegin-Е.Багоцкий+23:03:2007, 10:54 -->
<span class="vbquote">(Е.Багоцкий @ 23:03:2007, 10:54 )</span><!--QuoteEBegin-->То для класса MyKlass необходим конструктор без аргументов, что ограничивает разработчика.
[snapback]59918" rel="nofollow" target="_blank[/snapback]​
[/quote]
Полный бред! Зачем в векторе хранить объект, если можно хранить указатель на него. И тогда плевать на конструкторы по умолчанию.

Если честно прочел Ваш крик души и абсолютно запутался. Чего Вы хотите добиться? Чтобы Ваш код поддерживался разными компиляторами? Зачем такие сложности. Может стоит заново подумать над постановкой задачи?
 
1)Я только хотел сказать ,что концепция ООП основанная на использовании в новых разработках ранее разработанных классов не совсем гладко проходит если разработанные классы планируется использовать в будущем в разных средах разработки. Возсожно в этом виноваты Borland и Microsoft наплодившие в своих средах только ими применяемые типы и контейнеры по сравнению с классическим ANSI C++
2)Изыиняюсь за оговорку, говоря о "реализации класса" имел в виду не созданный объект класса, а сам процесс проектирования класса.
3)По поводу хранения указателей на класс - принимаю и обдумаю.
4) а как всё таки правильно в NET C++ работать со STL - почему ошибка ?
 
Так какие проблемы - пишите на стандартном С++, и не будет проблем с совместимостью. Предлагаемые расширения помогают постоянным разработчикам - но если вы пишите с VCL - глупо надеятся на то что этот код заработает на .NET. А если вы решите писать на .NET - не ждите что gcc с радостью его вам скомпилирует. Откуда вообще взялась цель использовать результат в разных средах?
 
<!--QuoteBegin-Е.Багоцкий+23:03:2007, 11:17 -->
<span class="vbquote">(Е.Багоцкий @ 23:03:2007, 11:17 )</span><!--QuoteEBegin-->если разработанные классы планируется использовать в будущем в разных средах разработки
[snapback]59922" rel="nofollow" target="_blank[/snapback]​
[/quote]
Если это изначально планируется, то нужно использовать средства оговоренные стандартом. Более того, не все компиляторы в полной мере поддерживают стандарт, но это уже из другой оперы
<!--QuoteBegin-Е.Багоцкий+23:03:2007, 11:17 -->
<span class="vbquote">(Е.Багоцкий @ 23:03:2007, 11:17 )</span><!--QuoteEBegin-->2)Изыиняюсь за оговорку, говоря о "реализации класса" имел в виду не созданный объект класса, а сам процесс проектирования класса.
[snapback]59922" rel="nofollow" target="_blank[/snapback]​
[/quote]
Я тоже это имел в виду. Вы поймите, что компилятор и версия среды разработки это разные вещи, связанные только косвенно
<!--QuoteBegin-Е.Багоцкий+23:03:2007, 11:17 -->
<span class="vbquote">(Е.Багоцкий @ 23:03:2007, 11:17 )</span><!--QuoteEBegin-->3)По поводу хранения указателей на класс - принимаю и обдумаю.
[snapback]59922" rel="nofollow" target="_blank[/snapback]​
[/quote]
Ну это общепринятая практика
<!--QuoteBegin-Е.Багоцкий+23:03:2007, 11:17 -->
<span class="vbquote">(Е.Багоцкий @ 23:03:2007, 11:17 )</span><!--QuoteEBegin-->а как всё таки правильно в NET C++ работать со STL
[snapback]59922" rel="nofollow" target="_blank[/snapback]​
[/quote]
Что вы подразумеваете под NET C++? Вы пишете управляемое приложение или все-таки классическое неуправляемое. Во втором случае все должно быть в порядке, только оговорите версию среды разработки
 
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'
 
<!--QuoteBegin-Е.Багоцкий+23:03:2007, 11:51 -->
<span class="vbquote">(Е.Багоцкий @ 23:03:2007, 11:51 )</span><!--QuoteEBegin-->using namespace System;
using namespace System::Runtime::InteropServices;
[snapback]59931" rel="nofollow" target="_blank[/snapback]​
[/quote]
Если я не ошибаюсь, то эти пространства имен содержат управляемые расширения и их использовать нельзя. Хотя я могу и ошибаться.
Далее если пишите using namespace std;, то зачем std::vector - масло масляное
 
Так то оно так, только всё равно пишем ли Using namespace std
вызываем ли vector<> или std: vector<> - результат один - не пропускает.
System::Runtime::InteropServices; тут не причём - исключал его вместе с строчками соотв кода - результат 1 и то же
 
На объявление Book хотелось бы взглянуть.
 
<!--QuoteBegin-Е.Багоцкий+23:03:2007, 12:02 -->
<span class="vbquote">(Е.Багоцкий @ 23:03:2007, 12:02 )</span><!--QuoteEBegin-->System::Runtime::InteropServices; тут не причём - исключал его вместе с строчками соотв кода - результат 1 и то же
[snapback]59936" rel="nofollow" target="_blank[/snapback]​
[/quote]
Ну на вектор это никак и не могло повлиять.
Может добавить #include <vector>?
 
Да, спасибо .Именно этого не хватало.
Правильный минимум таков:
#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); // а не предыдущий вариант -проверял работает правильно.
Так что тезис о хранении в векторе указателей на объекты а не сами объекты вызывает сомнение
 
<!--QuoteBegin-Е.Багоцкий+23:03:2007, 14:47 -->
<span class="vbquote">(Е.Багоцкий @ 23:03:2007, 14:47 )</span><!--QuoteEBegin-->вектор содержит указатели не на все объекты - а лишь на последний созданный объект
[snapback]59966" rel="nofollow" target="_blank[/snapback]​
[/quote]
Абсолютный бред! Все указатели хранятся там. Как то неудобно взрослого человека тыкать носом в книгу, но все же... С вектором возможно работать так же как и с массивом. Покажите код, который вызывает затруднение
 
//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->show_bookStr());
else if (B.show_janr()==nj) //здесь в цикле по i должны отображ названия разных книг !!!
lb->Items->Add(B->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->show_janr() на B.show_janr() получается всё верно - после прохода цикла по
B.capasity() в списке отображаются разные книги, чего нет в предыдущ случае !!!
 
Потому и говорим в 2 голоса - читать и осмысливать страуструпа. Хотя после VB, верю, непросто :)
B.push_back(new book(tit,au,ns,jnr));
 
Код:
void __fastcall TForm1::bOKClick(TObject *Sender)
{
...
book* pb = new book(tit,au,ns,jnr);
B.push_back( pb );
...
}
В Вашем коде создан только один объект, он изменяется и указатель на него помещается в вектор. Естественно, что все объекты одинаковы
 
Да ,я это понял. Спасибо.
Всё-таки интересно работать с классами, когда много экземпляров а не 1 -3 как в учебных примерах
которыми нас пичкают на учебных сайтах.
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!