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

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

Проблема в том, что в 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++ программисты управляются с множкством объектом класса?
 

grigsoft

Well-Known Member
15.11.2005
735
0
#2
Программисты на С++ читают страуструпа сначала, и вникают в концепцию указателей, конструкторов и прочих сложных терминов.

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

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

Как управляться? Да как хочешь: хочешь - пиши свой велосипед, хочешь - используй STL, или другие расширения. Встроенного в язык динамического массива тут действительно нет. Но все средства разработки с радостью предложат альтернативу - будь то ArrayList, СArray или vector.
 
02.02.2007
14
0
#4
В поставленном мной вопросе я хотел фактически сказать по-моему довольно важную вещь.
Жаль, что такая негативная реакция.
Если
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 * в реализации для выделения каждого элемента контейнера.
 
04.09.2006
2 566
2
Минск
#5
<!--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]
Полный бред! Зачем в векторе хранить объект, если можно хранить указатель на него. И тогда плевать на конструкторы по умолчанию.

Если честно прочел Ваш крик души и абсолютно запутался. Чего Вы хотите добиться? Чтобы Ваш код поддерживался разными компиляторами? Зачем такие сложности. Может стоит заново подумать над постановкой задачи?
 
02.02.2007
14
0
#6
1)Я только хотел сказать ,что концепция ООП основанная на использовании в новых разработках ранее разработанных классов не совсем гладко проходит если разработанные классы планируется использовать в будущем в разных средах разработки. Возсожно в этом виноваты Borland и Microsoft наплодившие в своих средах только ими применяемые типы и контейнеры по сравнению с классическим ANSI C++
2)Изыиняюсь за оговорку, говоря о "реализации класса" имел в виду не созданный объект класса, а сам процесс проектирования класса.
3)По поводу хранения указателей на класс - принимаю и обдумаю.
4) а как всё таки правильно в NET C++ работать со STL - почему ошибка ?
 

grigsoft

Well-Known Member
15.11.2005
735
0
#7
Так какие проблемы - пишите на стандартном С++, и не будет проблем с совместимостью. Предлагаемые расширения помогают постоянным разработчикам - но если вы пишите с VCL - глупо надеятся на то что этот код заработает на .NET. А если вы решите писать на .NET - не ждите что gcc с радостью его вам скомпилирует. Откуда вообще взялась цель использовать результат в разных средах?
 
04.09.2006
2 566
2
Минск
#8
<!--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++? Вы пишете управляемое приложение или все-таки классическое неуправляемое. Во втором случае все должно быть в порядке, только оговорите версию среды разработки
 
02.02.2007
14
0
#9
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'
 
04.09.2006
2 566
2
Минск
#10
<!--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 - масло масляное
 
02.02.2007
14
0
#11
Так то оно так, только всё равно пишем ли Using namespace std
вызываем ли vector<> или std: vector<> - результат один - не пропускает.
System::Runtime::InteropServices; тут не причём - исключал его вместе с строчками соотв кода - результат 1 и то же
 
04.09.2006
2 566
2
Минск
#13
<!--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>?
 
02.02.2007
14
0
#14
Да, спасибо .Именно этого не хватало.
Правильный минимум таков:
#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; //извините в предыд ответе случайно произошел перенос
 
02.02.2007
14
0
#15
Только что понял, что преждевременно сказал, что успех. Код выше, запихивает в вектор указатели на объект, а не сами объекты, при этом т.к сам объект меняет ссылку - то вектор содержит указатели не на все объекты - а лишь на последний созданный объект.
Правильный код всё таки использует хранение в векторе самих объектов а не указателей
book b;
vector <book > B;

b=book(tit,au,ns,jnr);
B.push_back(b); // а не предыдущий вариант -проверял работает правильно.
Так что тезис о хранении в векторе указателей на объекты а не сами объекты вызывает сомнение
 
04.09.2006
2 566
2
Минск
#16
<!--QuoteBegin-Е.Багоцкий+23:03:2007, 14:47 -->
<span class="vbquote">(Е.Багоцкий @ 23:03:2007, 14:47 )</span><!--QuoteEBegin-->вектор содержит указатели не на все объекты - а лишь на последний созданный объект
[snapback]59966" rel="nofollow" target="_blank[/snapback]​
[/quote]
Абсолютный бред! Все указатели хранятся там. Как то неудобно взрослого человека тыкать носом в книгу, но все же... С вектором возможно работать так же как и с массивом. Покажите код, который вызывает затруднение
 
02.02.2007
14
0
#17
//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() в списке отображаются разные книги, чего нет в предыдущ случае !!!
 

grigsoft

Well-Known Member
15.11.2005
735
0
#18
Потому и говорим в 2 голоса - читать и осмысливать страуструпа. Хотя после VB, верю, непросто :)
B.push_back(new book(tit,au,ns,jnr));
 
04.09.2006
2 566
2
Минск
#19
Код:
void __fastcall TForm1::bOKClick(TObject *Sender)
{
...
book* pb = new book(tit,au,ns,jnr);
B.push_back( pb );
...
}
В Вашем коде создан только один объект, он изменяется и указатель на него помещается в вектор. Естественно, что все объекты одинаковы
 
02.02.2007
14
0
#20
Да ,я это понял. Спасибо.
Всё-таки интересно работать с классами, когда много экземпляров а не 1 -3 как в учебных примерах
которыми нас пичкают на учебных сайтах.