Zabránění vícenásobné inicializaci třídy – C / C++ – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Zabránění vícenásobné inicializaci třídy – C / C++ – Fórum – Programujte.comZabránění vícenásobné inicializaci třídy – C / C++ – Fórum – Programujte.com

 

Anonym
~ Anonymní uživatel
454 příspěvků
19. 4. 2016   #1
-
0
-

Ahoj,

mohl by mi někdo poradit s následujícím problémem?

Mám třídu A, uvnitř které je metoda Zpracuj(), která je během vykonávání programu několikrát zavolána. Do metody Zpracuj() bych chtěl přidat "shromažďovač", který by PO CELOU DOBU běhu programu shromažďoval určitá data. Tzn. v těle metody Zpracuj() by byla metoda ulozData() a např. i ukazData().

No a teď ten problém. Implementaci onoho shromažďovače bych chtěl mít v třídě B. Pokud bych uvnitř metody Zpracuj provedl inicializaci a pak buffer používal. Např.:

B shromazdovac;
shromazdovac.ulozData();

Bylo by mi to úplně k ničemu, protože při druhém zavolání metody Zpracuj() by se mi vytvořila nová instance a o data uložená ve shromařďovači bych přišel.

Nevěděli byste, jak tuto situaci řešit (jedná se o C++)?

Nahlásit jako SPAM
IP: 212.79.110.–
KIIV
~ Moderátor
+43
God of flame
19. 4. 2016   #2
-
0
-

Pravdepodobne hledas neco jako design pattern "singleton" nebo tak. Zalezi presne, co a kde ma to ulozData delat

Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Kit+15
Guru
19. 4. 2016   #3
-
0
-

#1 Anonym
Vytvoř si třídu Shromažďovač a její instanci injektuj do konstruktoru objektu třídy A.

Singleton se na tohle moc nehodí.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
Anonym
~ Anonymní uživatel
454 příspěvků
19. 4. 2016   #4
-
0
-

#3 Kit
Pokoušel jsem se to tak udělat, ale mám problém, že když chci vytvořit instanci třídy Shromažďovač v konstruktoru, v těle metody Zpracuj() už není viditelná. Nevím zda to popisuju srozumitelně - prostě pokud udělám např.:

tridaA::tridaA()
{
    Shromazdovac sh;
}

tridaA::zpracuj()
{
    sh.ulozData(); // sh zde neexistuje, protoze init je v konstruktoru
}
Nahlásit jako SPAM
IP: 212.79.110.–
Kit+15
Guru
19. 4. 2016   #5
-
0
-

#4 Anonym
Instanci třídy Shromažďovač musíš vytvořit mimo třídu A. Třída A s tím nemá nic společného, proto ani tu instanci nesmí vytvářet. 

tridaA::tridaA(Shromazdovac shr) {
    sh = shr;
}

tridaA::zpracuj() {
    sh.ulozData();
}
Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
KIIV
~ Moderátor
+43
God of flame
19. 4. 2016   #6
-
0
-

Jo, globalni promenna mimo tridy bude taky stacit (nebo spis tipuju, kde to v jeho pripade skonci :D)

Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Anonym
~ Anonymní uživatel
454 příspěvků
19. 4. 2016   #7
-
0
-

#5 Kit
Pak ale úplně nechápu to injektování do konstruktoru. Jak to bylo myšlené?

Jinak pokud to má být mimo třídu, udělal bych to takhle:

#include "Shromazdovac.h"

Shromazdovac sh;

tridaA::tridaA()
{
}

tridaA::zpracuj()
{
    sh.ulozData(); // sh zde neexistuje, protoze init je v konstruktoru
}

Globální proměnnou to právě řešit nechci...

Nahlásit jako SPAM
IP: 212.79.110.–
Kit+15
Guru
19. 4. 2016   #8
-
0
-

#7 Anonym
Úplně zapomínáš, že konstruktor může mít parametry. Injektuje se právě prostřednictvím parametrů.

Myslel jsem si, že víš, co je Dependency Injection.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
KIIV
~ Moderátor
+43
God of flame
19. 4. 2016   #9
-
0
-

#7 Anonym
tim "injektovat" je mysleno to, ze v objektu A budes mit referenci na ten shromazdovac

class B {
  public:
    void ulozData() { /* zapis? tezko rici ceho */ }
};
class A {
  public:
    A(B & b): shromazdovac{b} {;}
  protected:
    B & shromazdovac;
};
// pouziti:
int main() {
  B shromazdovac;
  A test{shromazdovac};

  ///...
}
Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
KIIV
~ Moderátor
+43
God of flame
19. 4. 2016   #10
-
0
-

A pokud staci mit ten shromazdovac jen ve tride A, tak muzes pouzit i modifikator static...

// header
class A {
  public:
    
  protected:
    static B shromazdovac;
};

// v jednom z cpp souboru:
B A::shromazdovac;
Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Anonym
~ Anonymní uživatel
454 příspěvků
19. 4. 2016   #11
-
0
-

#10 KIIV

Shromazdovac bych rád používal ve více třídách (ale ne současně). Myšleno, že za běhu programu se bude moct využívat např. jen třída A. Při spuštění programu s jinou konfigurací prozměnu jen třída X atd.

Mohl bych poprosit o vysvětlení téhle řádky?

A(B & b): shromazdovac{b} {;}
Nahlásit jako SPAM
IP: 212.79.110.–
KIIV
~ Moderátor
+43
God of flame
19. 4. 2016   #12
-
0
-

#11 Anonym
A (B & b)    konstruktor tridy A, parametr bude reference na objekt typu B s nazvem b

A (B & b) : shromazdovac{b}    inicializace tridni promenne typu reference na objekt B (pokud nahodou nemas c++11 tak musis pouzit:   shromazdovac(b)

{;} je pak samotny kod konstruktoru, nicmene inicializace je uz hotova (a na inicializaci reference uz je tu pozde) + strednik je jen zvyrazneni, ze ten blok nic nedela

Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Kit+15
Guru
19. 4. 2016   #13
-
0
-

#11 Anonym
V tom případě si vytvoříš rozhraní Shromažďovací a implementuješ ho ve třídách Shromažďovač a NullShromažďovač. Je snad jasné, že metoda NullShromažďovač::ulozData() bude existovat, ale nebude dělat vůbec nic.

No a pak injektuješ do třídy A objekt jedné či druhé třídy podle toho, zda v té třídě chceš shromažďovat data nebo nechceš.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
Anonym
~ Anonymní uživatel
454 příspěvků
20. 4. 2016   #14
-
0
-

#12 KIIV
Dík za vysvětlení. V C++ začínám a s tímhle zápisem jsem se ještě nesetkal. Myslel jsem, že zápis typu:

A (B & b) : neco

se používá v souvislosti s dědičností. Např, že chci oddědit třídu "neco" atd.

Nahlásit jako SPAM
IP: 212.79.110.–
KIIV
~ Moderátor
+43
God of flame
20. 4. 2016   #15
-
0
-

#14 Anonym
ano to se da pouzit - ale za  class, ne za konstruktorem

class potomek : public predek {
    public:
        // konstruktor:
        potomek(int neco) : predek(neco), promenna(0) {} // konstruktor potomka inicializuje i predka a tridni promenne 

    protected:
        int  promenna;
}
Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
KIIV
~ Moderátor
+43
God of flame
20. 4. 2016   #16
-
0
-

Jo a pokud udelas treba toto:

class X {
  public:
    X (const char * tmp) { str = tmp; }
    // X (const char * tmp) str{tmp} {}
    std::string  str;
};

tak to nejprve vytvori str s defaultnim konstruktorem a pak teprve pomoci assign operatoru priradi hodnotu.

Kdyz pouzijes zakomentovanou variantu, tak to rovnou vola konstruktor s tim retezcem.

Obzvlaste u slozitych nebo velkych objektu to muze hrat roli. A pokud mas defaultni konstruktor zakazany, tak to jinak, nez tou druhou variantou, ani nejde.

Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
vitamin+8
Grafoman
21. 4. 2016   #17
-
0
-

Podľa mňa by bolo lepšie pre začiatočníka aby trieda A vlastnila objekt shromazdovac (napr cez shared_ptr) alebo aby bol shromazdovac globalna/staticka premenná kde nemôže nastať situácia že životnosť objektu A je vyššia ako životnosť shromazdovac-a...

Nahlásit jako SPAM
IP: 195.28.77.–
obfuscate: "The cruel god Malloc will strike you down. "
ZMeson: "That's the C god. C++ has a new god. "
Kit+15
Guru
21. 4. 2016   #18
-
0
-

#17 vitamin
To přece nemůže nastat ani při DI, aby měl objekt A větší životnost než shromažďovač. Objekt třídy A si ten objekt shromažďovače bude držet.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
vitamin+8
Grafoman
21. 4. 2016   #19
-
0
-

#18 Kit
Problém môže ľahko nastať ak začne používať dynamickú alokáciu pamäte (a to aj skryte pomocou kontajnerov a pod). 

Nahlásit jako SPAM
IP: 195.28.77.–
obfuscate: "The cruel god Malloc will strike you down. "
ZMeson: "That's the C god. C++ has a new god. "
Kit+15
Guru
21. 4. 2016   #20
-
0
-

#19 vitamin
Aha, neuvědomil jsem si, že C++ nemá GC.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
Anonym
~ Anonymní uživatel
454 příspěvků
21. 4. 2016   #21
-
0
-

Ahoj,

tak jsem se s tím zkoušel poprat, ale zatím bez úspěchu. Ještě jednou shrnu, o co mi jde. Mám dvě třídy Zpracovavani{} a Shromazdovani{}:

#ifndef ZPRACOVAVANI_H
#define ZPRACOVAVANI_H

#include <QByteArray>

class Zpracovavani
{
public:
    Zpracovavani();
    void zpracujData(bool ukladani, QByteArray data);
};

#endif // ZPRACOVAVANI_H
#include "Zpracovavani.h"

void Zpracovavani::zpracujData(bool ukladani, QByteArray data){

    if (ukladani == true){
        // ulozData(data.data())
    }else{
        //ukazData()
    }
}
#ifndef SHROMAZDOVANI_H
#define SHROMAZDOVANI_H

#include <QByteArray>

class Shromazdovani
{
public:
    Shromazdovani();

    void ulozData(QByteArray data);
    QByteArray ukazData();

private:
    QByteArray shromazdovac;
};

#endif // SHROMAZDOVANI_H
#include "Shromazdovani.h"

Shromazdovani::Shromazdovani()
{
}

void Shromazdovani::ulozData(QByteArray data){
    shromazdovac.append(data);
}

QByteArray Shromazdovani::ukazData(){
    QByteArray pomShromazdovac = shromazdovac.data();
    return pomShromazdovac;
}

Problém je v tom, že metoda zpracujData() je zvenčí v cyklu volána a já potřebuju data ze vstupního parametru metody ukládat do shromažďovače ve třídě Shromazdovani.

Chápu tedy správně, že správná cesta je udělat něco takového?

#ifndef ZPRACOVAVANI_H
#define ZPRACOVAVANI_H

#include <QByteArray>
#include "Shromazdovani.h"

class Zpracovavani
{
public:
    Zpracovavani(Shromazdovani & shr) : shromazdovac{shr} {;}
    void zpracujData(bool ukladani, QByteArray data);

private:
    Shromazdovani & shromazdovac;
};

#endif // ZPRACOVAVANI_H
Nahlásit jako SPAM
IP: 212.79.110.–
KIIV
~ Moderátor
+43
God of flame
21. 4. 2016   #22
-
0
-

#21 Anonym
jeste neco takoveho:

void Zpracovavani::zpracujData(bool ukladani, QByteArray data){

    if (ukladani == true){
        shromazdovac.ulozData(data);
    }else{
        /* ... = */shromazdovac.ukazData();
    }
}
Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Anonym
~ Anonymní uživatel
454 příspěvků
21. 4. 2016   #23
-
0
-

#22 KIIV
To chápu, děkuju. Jen mě zajímalo, zda mám správnou myšlenku. Prakticky mám problém s tím, že v inicializaci třídy Zpracovavani provádím v třídě např. ABCD.  Je jasné, že

Zpracovavani zp();


teď použít nemohu, protože konstruktor má vstupní parametr. Jsem uplně mimo když bych to chtěl vyřešit něčím jako:

// uvnitr tridy ABCD
Shromazdovani & shr;
Zpracovavani zp(&);
Nahlásit jako SPAM
IP: 212.79.110.–
Anonym
~ Anonymní uživatel
454 příspěvků
21. 4. 2016   #24
-
0
-

Myslel jsem  

Zpracovavani zp(&shr);
Nahlásit jako SPAM
IP: 212.79.110.–
Kit+15
Guru
21. 4. 2016   #25
-
0
-

#22 KIIV
To je sice procedurální řešení, ale je funkční.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
KIIV
~ Moderátor
+43
God of flame
21. 4. 2016   #26
-
0
-

#25 Kit
volani metody objektu vnimas jako proceduralni reseni, budiz...

Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Anonym
~ Anonymní uživatel
454 příspěvků
21. 4. 2016   #27
-
0
-

Aha a jak by to mělo být správně dle OOP?

Nahlásit jako SPAM
IP: 212.79.110.–
Kit+15
Guru
21. 4. 2016   #28
-
0
-

#27 Anonym
Na to je návrhový vzor NullObject. Do konstruktoru se injektuje objekt, který umí logovat nebo objekt, který obsahuje stejnou logovací metodu, ale nedělá nic.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
Kit+15
Guru
21. 4. 2016   #29
-
0
-

#26 KIIV
Je tam "if", tedy podmíněné větvení, které z toho dělá procedurální řešení. V OOP se to dělá obvykle bez něj.

Nahlásit jako SPAM
IP: 194.228.13.–
Komentáře označují místa, kde programátor udělal chybu nebo něco nedodělal.
KIIV
~ Moderátor
+43
God of flame
21. 4. 2016   #30
-
0
-

#29 Kit
kdo vi, co tam s tim vymejsli. Kazdopadne je to nad tim samym objektem, jen jina akce.

Tim nullobject patternem by nakonec byl treba stejny kod, jen by to nic nedelalo.

Proste a jednoduse: neda se moc odhadovat, o co se to presne snazi. Pak se tezko radi

Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
KIIV
~ Moderátor
+43
God of flame
21. 4. 2016   #31
-
0
-

Nakonec se zjisti, ze jedine, co potreboval bylo toto:

#include <iostream>
#include <string>

class shromazdovac {
  public:
    void ulozData(std::string const add) { data += add + " | "; }
    std::string zobrazData() { return data; }
  private:
    static std::string data;
};

class donothing {
  public:
    void ulozData(std::string const add) {  }
    std::string zobrazData() { return std::string(); }
};

std::string  shromazdovac::data;

template <class T = donothing>
class A : public T {
  public:
    void zpracujData(std::string const x) { T::ulozData(x); }
    void tiskniData() { std::cout << T::zobrazData() << std::endl; }
  protected:
};

int main() {
    A<shromazdovac> verzeA;
    A<shromazdovac> verzeB;
    A<donothing>    verzeC;
    A<>             verzeD;

    verzeA.zpracujData("test");
    verzeA.zpracujData("best");
    verzeA.zpracujData("lest");
    verzeA.tiskniData();

    verzeB.zpracujData("sest");
    verzeB.zpracujData("rest");
    verzeB.zpracujData("west");
    verzeB.tiskniData();

    verzeC.zpracujData("test");
    verzeC.zpracujData("best");
    verzeC.zpracujData("lest");
    verzeC.tiskniData();

    verzeD.zpracujData("test");
    verzeD.zpracujData("best");
    verzeD.zpracujData("lest");
    verzeD.tiskniData();

}

Coz je vcelku dosti podobne jak globalni promenne, tak singletonu...

Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
Anonym
~ Anonymní uživatel
454 příspěvků
22. 4. 2016   #32
-
0
-

No ono tak jak to teď mám to už funguje. Tzn. mám něco jako

    Shromazdovani  shr;
    Zpracovavani zp(shr);

    zp.zpracujData(true, data.data());
    zp.zpracujData(true, data.data());
    zp.zpracujData(true, data.data());
    zp.zpracujData(true, data.data());
    zp.zpracujData(false, data.data());

čímž to testuju. Furt ale dumám nad tím zápisem

Zpracovavani(Shromazdovani & shr) : shromazdovac{shr} {;}

To je standardní C++ zápis? Nikde ve článcích o C++ konstruktorech jsem to nenašel. A bylo by možné tento řádek zapsat až v tom bloku? Myšleno

Trida::Trida(){

//...

}
Nahlásit jako SPAM
IP: 212.79.110.–
KIIV
~ Moderátor
+43
God of flame
22. 4. 2016   #33
-
0
-

#32 Anonym
ano, je to standartni zapis (jen ty {} jsou az od -std=c++11. Ve starem by to bylo shromazdovac(shr) - http://en.cppreference.com/w/cpp/language/initializer_list
ne, referenci bez toho proste nenainicializujes - v bloku konstruktoru je na to uz prilis pozde

mimochodem: delas tam neskutecne moc kopii toho QByteArray. Metoda data() vraci pointer na pole znaku. Tim, ze to pak prdnes do QByteArray vytvaris dalsi objekt (pravdepodobne s kopii dat), protoze to umi udelat implicitni konverzi z char * na QByteArray

Napriklad:

    QByteArray pomShromazdovac = shromazdovac.data(); // kopie 1
    return pomShromazdovac; // kopie 2
// venku pak kopie 3 ... 
Nahlásit jako SPAM
IP: 94.113.92.–
Program vždy dělá to co naprogramujete, ne to co chcete...
ondrej39+1
Věrný člen
22. 4. 2016   #34
-
+1
-
Zajímavé
Kit +

#17 vitamin
Nikdo po něm nechce, aby vytvářel objekt na free-store. Instanci klidně může vytvořit na stacku a předávat referenci na tuto. To stejné by měl dělat s A.

Pointery bych pro běžné programování úplně zakázal a používal bych je fakt až úplně v krajní nouzi, kdy potřebuješ extrémní optimalizaci, anebo používáš starší knihovnu.

Eliminovat kopírování jde v  C++11 a vyšší stejně dobře move konstruktorem a move assignment operátorem jako kdybys použil pointery (ať již inteligentní, nebo normální) a můžeš mít na stacku skoro všechno.

Nahlásit jako SPAM
IP: 78.156.159.–
Inject all the dependencies!
KIIV
~ Moderátor
+43
God of flame
22. 4. 2016   #35
-
0
-

#34 ondrej39
Kdyz ma   QByteString neco; a pak porad pouziva    QByteString neco_jineho = neco.data(), tak ho nic nezachrani.

Nahlásit jako SPAM
IP: 212.47.3.–
Program vždy dělá to co naprogramujete, ne to co chcete...
vitamin+8
Grafoman
22. 4. 2016   #36
-
0
-

#34 ondrej39

Problém je že chce zdielať 1 objekt typu B medzi viacerými objektami typu A. Tam je nutné použiť určitú formu pointera/referencie a podľa mňa je lepšie použiť pointer s vlastníctvom a nie čistý pointer/referenciu prípadne GC. Kým je všetko na stacku tak to môže byť OK ale stačí aby začal využívať kontajnery a inú dynamickú alokáciu pamäte a skončí pri tom že sa bude učiť ako narábať s valgrind-om (čo môže byť nakoniec aj prospešné   )

Nahlásit jako SPAM
IP: 195.28.77.–
obfuscate: "The cruel god Malloc will strike you down. "
ZMeson: "That's the C god. C++ has a new god. "
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, 6 hostů

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ý