Dynamické pole různých objektů – C / C++ – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Dynamické pole různých objektů – C / C++ – Fórum – Programujte.comDynamické pole různých objektů – C / C++ – Fórum – Programujte.com

 

Tundra
~ Anonymní uživatel
25 příspěvků
16. 11. 2009   #1
-
0
-

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.

Nahlásit jako SPAM
IP: 82.117.140.–
liborb
~ Redaktor
+18
Guru
16. 11. 2009   #2
-
0
-

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.

Nahlásit jako SPAM
IP: 85.207.166.–
Tundra
~ Anonymní uživatel
25 příspěvků
16. 11. 2009   #3
-
0
-

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).

Nahlásit jako SPAM
IP: 82.117.140.–
Tundra
~ Anonymní uživatel
25 příspěvků
16. 11. 2009   #4
-
0
-

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?

Nahlásit jako SPAM
IP: 82.117.140.–
liborb
~ Redaktor
+18
Guru
16. 11. 2009   #5
-
0
-

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.

Nahlásit jako SPAM
IP: 85.207.166.–
liborb
~ Redaktor
+18
Guru
16. 11. 2009   #6
-
0
-

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:

Nahlásit jako SPAM
IP: 85.207.166.–
Tundra
~ Anonymní uživatel
25 příspěvků
16. 11. 2009   #7
-
0
-

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ý...

Nahlásit jako SPAM
IP: 82.117.140.–
Tundra
~ Anonymní uživatel
25 příspěvků
16. 11. 2009   #8
-
0
-

Ještě jsem si všiml pár chyb a zbytečně komplikovaných vyjádření.
Počkejte chvíli, ještě to předělám...

Nahlásit jako SPAM
IP: 82.117.140.–
Tundra
~ Anonymní uživatel
25 příspěvků
16. 11. 2009   #9
-
0
-



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...

Nahlásit jako SPAM
IP: 82.117.140.–
liborb
~ Redaktor
+18
Guru
16. 11. 2009   #10
-
0
-

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:

Nahlásit jako SPAM
IP: 85.207.166.–
Tundra
~ Anonymní uživatel
25 příspěvků
16. 11. 2009   #11
-
0
-

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:

Nahlásit jako SPAM
IP: 82.117.140.–
bukaj_0010
Věrný člen
16. 11. 2009   #12
-
0
-

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;
}

Nahlásit jako SPAM
IP: 88.101.128.–
Jak se správně ptát? -> http://www.hash.cz/inferno/otazky.html[br][br] Po programování je člověk hladovej.
Tundra
~ Anonymní uživatel
25 příspěvků
16. 11. 2009   #13
-
0
-

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.

Nahlásit jako SPAM
IP: 82.117.140.–
liborb
~ Redaktor
+18
Guru
17. 11. 2009   #14
-
0
-

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á.

Nahlásit jako SPAM
IP: 195.189.142.–
liborb
~ Redaktor
+18
Guru
17. 11. 2009   #15
-
0
-

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á.

Nahlásit jako SPAM
IP: 195.189.142.–
Zjistit počet nových příspěvků

Přidej příspěvek

Toto téma je starší jak čtvrt roku – přidej svůj příspěvek jen tehdy, máš-li k tématu opravdu co říct!

Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku

×Vložení zdrojáku

×Vložení obrázku

Vložit URL obrázku Vybrat obrázek na disku
Vlož URL adresu obrázku:
Klikni a vyber obrázek z počítače:

×Vložení videa

Aktuálně jsou podporována videa ze serverů YouTube, Vimeo a Dailymotion.
×
 
Podporujeme Gravatara.
Zadej URL adresu Avatara (40 x 40 px) nebo emailovou adresu pro použití Gravatara.
Email nikam neukládáme, po získání Gravatara je zahozen.
-
Pravidla pro psaní příspěvků, používej diakritiku. ENTER pro nový odstavec, SHIFT + ENTER pro nový řádek.
Sledovat nové příspěvky (pouze pro přihlášené)
Sleduj vlákno a v případě přidání nového příspěvku o tom budeš vědět mezi prvními.
Reaguješ na příspěvek:

Uživatelé prohlížející si toto vlákno

Uživatelé on-line: 0 registrovaných, 116 hostů

Podobná vlákna

Pole různých objektů — založil jarda23

Dynamicke pole — založil george6565

Ne/dynamicke pole — založil cecilconrad

Dynamicke pole — založil Earl Cash

Moderátoři diskuze

 

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý