Dobrý den, potřeboval bych poradit s jedním problémem. Už se to nějakou dobu snažím vyřešit, ale protože se jedná o věc, které nerozumím úplně do hloubky a která takovou znalost pro úspěšné vyřešení vyžaduje, rozhodl jsem se požádat vás o pomoc.
Dělám jakési 3D prostředí - hru nebo nějaký fyzikální simulátor, ještě přesně nevím. Mám vytořenou třídu Objekt a potom její potomky a potom potomky těch potomků. Ti druzí potomci reprezentují konkrétní objekty, které se v prostředí budou vyskytovat. Jsou to napřiklad různé krabice, světla, židle, stoly, postavy atd...
Mým cílem je zařídit, abych mohl kdykoliv z kterékoliv části programu vytvořit kterýkoliv z těchto objektů a uložit jeho adresu do nějakého pole obsahujícího všechny tyto objekty. K těm bych potom mohl přistupovat hromadně. Například bych je všechny najednou překreslil nebo najednou zastavil jakýkoliv pohyb.
Abych to vyřešil, vytvořil jsem si třídu Objekty, která by tohle všechno měla zvládat. Nevím ale přesně, jak bych to měl udělat. Moje myšlenka byla přibližně takováto:
Třída Objekty by obsahovala pole objekt[], jehož délka by se dynamicky měnila podle počtu objektů, které vytvářím a kté by buď přímo obsahovalo objekty, nebo by obsahovalo ukazatele na objekty. Pak by v třdě Objekty byla funkce CreateObject(Objekt *objekt_ptr), která by se volala si takhle: objekty.CreateObjekt(new Krabice). Udělala by to, že by zkopírovala předaný objekt do nějaké vlastní proměnné a adresu by potom uložila do pole objekt[].
Vůbec nevím, jestli by to takhle bylo správně ani přesně nevím, jak to udělat. vím že možností je tolik, že než bych to ozkoušel, trvalo by to dlouho a stejně bych a to nejspíš nepřišel. Goole jsem zkoušel dost dlouho.
Byl bych velmi vděčný, kdyby mi někdo pomohl.
Děkuji.
Fórum › C / C++
Dynamické pole různých objektů
Tvoje úvahy vedou správným směrem. Jen tak mimochodem je to klasická úloha, na které se dá ukázat síla OOP.
Možná lepší než dynamické pole by byl spojitý seznam (přidávání záznamů aniž by si musel realokovat paměť pro již uložené). Ale to hodně záleží na tom, jestli budeš přidávat nebo hodně přidávat, nebo i ubírat nebo i hodně ubírat. Výsledkem (tak či tak) bude seznam objektů, se kterými můžeš pracovat.
A když budeš mít seznam (nebo pole), tak hromadná práce s objekty (samozřejmě s využitím polymorfismu) je již snadnou záležitostí. Ve společné třídě (Objekt) si nadefinuješ společné "rozhraní" tj. všechny funkce, které budeš od všech objektů vyžadovat. Tyto funkce označíš jako virtual. Stejné funkce (stejně definované) implementuješ ve zděděných třídách.
Aha, to zní zajímavě. Ty objekty mám už vytvořené a fungují přesně jak jsi říkal. Rozchodil jsem jich několik, ale vždycky jsem je vkládal jednotlivě jako globální proměnné.
Co přesně znamená ten spojitý seznam? Může prosím uvést příklad? Někde jsem četl o metodě, kdy se objekty vlastně "zřetězí". Každý objekt (instance) obsahuje ukazatel na následující objekt a tím se pak získává přístup kpostupně ke všem. To bych možná zvládnul, ale mám obavy, co by se dělo, kdybych chtěl přistoupit třeba k 50. objektu v řadě :D.
Co se týče přidávání a ubírání objektů - hodlám na začátku (nejspíš v konstruktoru třídy Objekty) vložit hromadu objektů a poté v průběhu programu občas přidat nebo odebrat nějaký objekt (na žádné masové změny, ale mělo by jít občas něco přidat nebo ubrat).
Už začínám tušit. Než to začnu všechno zkoušet, byl bych vám vděčný, kdyby jste mi lehce prohlídnul kód, jestli je to to, co jste myslel.
Nějak takhle by vypadala třída Objekty:
class Objekty
{
struct objekt
{
Objekt data;
objekt dalsi;
};
bool CreateObject(Objekt *objekt_ptr)
{
objekt *a;
while (a->dalsi)
{
a = a->dalsi;
}
a->dalsi= (objekt*) malloc(sizeof(objekt));
//a->dalsi.data=objekt_ptr; - to je nejspíš špatně, tady by se zkopíroval objekt objekt_ptr do na volné místo v a->dalsi.data
};
};
Další funkce pro mazání a vypisování objektů by už měly být snadné.
Co si myslíte? Nenapadá vás rychle něco, jak zkopírovat snadno ten objekt?
To je přesně ono - zřetězení. Ty to všechno znáš :smile1: . Jedna z možností je následující. Ty máš v programu cosi jako (možná trochu jinak):
// někde v hlavičkovém souboru - member člen třídy Objekty
Objekt** m_pSeznamObjektu;
// a někde v implementaci
m_pSeznamObjektu = new Object*[_delka_pole_];
// a pak v té funkdi Create
m_pSeznamObjektu[m_nPosledniVolny++] = objekt_ptr;
Máš prostě pole ukazatelů a postupně je plníš. Když by byl poslední volný větší než je délka pole, tak musíš alokovat větší pole atd. atd. A pomocí seznamu je tady drobná změna. Nepracuješ přímo s m_pSeznamObjektu (viz výše), ale to pole ukazatelů "schováš" do struktury např.:
typedef struct _SEZNAM_OBJEKTU {
Objekt** pSeznamObjektu;
_SEZNAM_OBJEKTU* pDalsiSeznam;
} SEZNAM_OBJEKTU;
Takže když už je velikost pole pSeznamObjektu malá, tak "přihodíš" dynamicky další článek seznamu (spojený přes pDalsiSeznam) atd atd. Do té svojí struktury můžeš ukládat jednotlivé objekty (co objekt, to nová část seznamu) nebo můžeš zase ukládat pole (jako v tomto příkladu). Práce je pak podobná jako s polem, jenom se musí navíc trochu počítat (jeden díl seznamu bude obsahovat třeba 32 objektů, takže když chci přistoupit k 50., tak je to ve druhé části seznamu (50 - 32) 18. položka apod.). A délka pole u toho seznamu nemusí být dynamická, ale může být (což je možná i lepší) statická:
Objekt* pSeznamObjektu[64];
A pokud máš u pole uloženou aktuální pozici prvního volného, tak u seznamu tohoto typu to bude pointer na seznamovou položku a zase ten index do pole.
Jen tak mimochodem, problém není 50. položka ale třeba 850415. položka. Pokud máš počty objektů maximálně stovky, tak je i ten "jednoobjektový" seznam dostačující. Ale vždycky záleží na tom, co chceš s těmi daty (objekty) dělat.
Viz můj předchozí příspěvek ....
class Objekty
{
struct objekt
{
Objekt* data;
objekt* dalsi;
};
objekt m_sSeznam;
bool CreateObject(Objekt *objekt_ptr)
{
objekt *a = &m_sSeznam;
while (a->dalsi) {
a = a->dalsi;
}
a->dalsi= (objekt*) malloc(sizeof(objekt));
a = a->dalsi;
a->dalsi = NULL;
a->data=objekt_ptr;
};
};
Samozřejmě to není všechno, ale nechci to řešit za tebe :smile14: . Nehledě na to, že si vedeš dobře (řekl bych, že velice dobře). Potřebuješ ještě v konstruktoru inicializovat m_sSeznam (hlavně dalsi), ošetřit chybové stavy atd. atd. Když ti řeknu úpně přesně jak to, tak se to nikdy nenaučíš .... :smile1:
Aha to je velmi zajmavé. Takže jsem ještě předělal tu třídu.
class Objekty
{
struct seznam_objektu
{
Objekt data;
seznam_objektu *dalsi;
};
seznam_objektu objekt[64];
bool CreateObject(Objekt *objekt_ptr)
{
objekt *a = this->objekt[0];
while (a->dalsi[0])
{
a = a->dalsi[0];
}
int i=0;
while (a->dalsi[i])
{
i++;
}
if (i=64)
{
a->dalsi[0]=(seznam_objektu*) malloc(sizeof(seznam_objektu)*64);
a->dalsi[0]=new seznam_objektu[64];
a->dalsi[0]->data=objekt_ptr; //tady by se to mělo překopírovat
}
else
{
a->dalsi[i]=objekt_ptr; //tady by se to mělo překopírovat
}
};
};
A teď už fakt nevim, jestli je tohle dobře, tak ať visim :D.
Jak se vám to líbí? Řekl bych, že tam bude pár chyb v přiřazování ukazatelů a objektů - je to dost složitý...
class Objekty
{
struct seznam_objektu
{
Objekt *data[64];
seznam_objektu *dalsi;
};
seznam_objektu objekt;
bool CreateObject(Objekt *objekt_ptr)
{
objekt *a = &this->objekt;
while (a->dalsi)
{
a = a->dalsi;
}
int i=0;
while (a->data[i])
{
i++;
}
if (i=64)
{
a->dalsi=(seznam_objektu*) malloc(sizeof(seznam_objektu));
a->dalsi=new seznam_objektu;
a->dalsi->dalsi=NULL;
a->dalsi->data=objekt_ptr; //tady by se to mělo překopírovat
}
else
{
a->data[i]=objekt_ptr; //tady by se to mělo překopírovat
}
};
};
Teď by to mělo nějak fungovat.
Začíná mě už dost pálit - mám do toho a->data[i] kopírovat jen adresu předanou objekt_ptr nebo i všechna data? Viděl bych to radši na všechna data, aby mi nemohl ten objekt na adrese objekt_ptr zaniknout spolu s funkcí, která ho vytvořila. Nevím ale, jak pořádně na to...
Jo ... to jo mnohem lepší.
Ještě bych se nespolehál na nulování dat a po:
a->dalsi=new seznam_objektu;
a->dalsi->dalsi=NULL;
a přidal nulování všech pointerů resp. paměti pro ně (memset apod.).
A co se týká tvé další otázky, tak vzhledem k tomu, že ho tvoříš dynamicky (ten objekt) při volání CreateObject (tj. new NazevObjektu()), tak objekt zanikne až na něj (resp. na jeho pointer) zavoláš uvolnění paměti (delete). Pokud máš problém s tím, kdy se má volat zrušení objektu, tak na to samozřejmě existuje taky hezký způsob, a to je počítání referencí na objekt (viz COM a IUnknown - strejda google ti toho určitě najde :smile1: ). Ale to si nemyslím, že je tvůj případ. V tvém případě asi půjde o to, že dokud v destruktoru třídy Objekty (nebo v metodě, která bude objekt mazat) nezavoláš mazání nad objektem, tak ho můžeš vesele používat. A když ho smažeš, tak si nezapomeň snulovat ten pointer i v těch datech - to jsou pak veselé funkce, co program provádí :smile8:
Já měl právě strach z toho, že by se mohl sám smazat ten objekt dřív, než bych chtěl já. Nebyl jsem si zcela jistý spolehlivostí pravidla "objekt vytvořený pomocí 'new' zanikne teprve, až když zanikne poslední reference na něj". Takže to jsem rád, že tady problém nejspíš nebude.
Tak já to zkusím dát do mýho programu a pak ti sem napíšu, jak to dopadlo. Nejspíš sem ještě přihodím tu metodu pro mazání objektů. Tam si ale ještě nejsem jistý, jak to udělat. Nejspíš asi prostě důkladně procyklímt všechny ty objekty a přeházím je aby šly pěkně za sebou bez mezer po vymazaných objektech.
No snad to klapne :smile1:
Tundra
Spojový seznam je hezké řešení. Ale pozor na to, že pokud bude takhle ve třídě a nebude statický (deklarován jako static), s každou instancí se vytvoří nový. Osobně bych se nebál věřit implementaci v STL (Standard Template Library) a využil tedy klidně std::list. Navíc starat se o přidávání do seznamu a vymazávání z něj růčo? Ale no tak, máme konstruktory a destruktory. Celá implementace je pak otázkou pár řádků – včetně odstraňování ze seznamu při vypršení platnosti objektu.
object.h:
#ifndef OBJECT_H
#define OBJECT_H
#include <list>
class object
{
private:
static std::list<object*> _all;
public:
static std::list<object*> &all(void);
object();
~object();
};
#endif /* object.h */
object.cpp:
#include "object.h"
std::list<object*> object::_all = std::list<object*>();
std::list<object*> &object::all(void)
{
return object::_all;
}
object::object()
{
object::_all.push_front(this);
}
object::~object()
{
object::_all.remove(this);
}
A ukázka použití, řekněme main.cpp:
#include <iostream>
#include "object.h"
int main(void)
{
object a;
for (std::list<object*>::iterator i = object::all().begin();
i != object::all().end(); ++i)
{
std::cout << &(*i) << std::endl;
}
std::cout << "---" << std::endl;
{
object b;
for (std::list<object*>::iterator i = object::all().begin();
i != object::all().end(); ++i)
{
std::cout << &(*i) << std::endl;
}
std::cout << "---" << std::endl;
{
object c;
for (std::list<object*>::iterator i = object::all().begin();
i != object::all().end(); ++i)
{
std::cout << &(*i) << std::endl;
}
std::cout << "---" << std::endl;
}
}
for (std::list<object*>::iterator i = object::all().begin();
i != object::all().end(); ++i)
{
std::cout << &(*i) << std::endl;
}
std::cout << "---" << std::endl;
}
No fakt síla. Dal jsem tam ten kód, co jsem tady vytvořil, chvilku jsem vylaďoval lehké chybky, pak jsem se modlil aby to vyšlo a ono to fakt FUNGUJE :smile18: :smile5: . Musím říct, že jsem to skutečně skoro ani nečekal, ale je to super.
Děkuji moc. Teď jdu dělat něco jako kamery, pak jdu na FPS... No bude to fakt zajímavý :smile1: .
Ještě jednou fakt díky.
To Tundra : Není zač.
To bukaj_001 : Volba datové struktury pro uložení objektů je implementační detail, takže proč ne STL. Podle vkusu každého soudruha :-).
Další věcí ovšem je, že ten panáček se to učí a znalost seznamů (i to jak je udělat "ručně") mu jistě neuškodí, ale naopak.
Jinak samozřejmě pokud existuje hotové řešení, tak je to škoda programátorského sádla. O tom žádná.
To Tundra : Není zač.
To bukaj_001 : Volba datové struktury pro uložení objektů je implementační detail, takže proč ne STL. Podle vkusu každého soudruha :-).
Další věcí ovšem je, že ten panáček se to učí a znalost seznamů (i to jak je udělat "ručně") mu jistě neuškodí, ale naopak.
Jinak samozřejmě pokud existuje hotové řešení, tak je to škoda programátorského sádla. O tom žádná.
Přidej příspěvek
Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku
×Vložení zdrojáku
×Vložení obrázku
×Vložení videa
Uživatelé prohlížející si toto vlákno
Podobná vlákna
Pole různých objektů — založil jarda23
Vyhledávání v poli různých objektů — založil Zuzana
Dynamicke pole — založil george6565
Ne/dynamicke pole — založil cecilconrad
Dynamicke pole — založil Earl Cash
Moderátoři diskuze