magántanulás átka, hogy nincs visszajelzés

 ( lzrd | 2008. március 19., szerda - 15:11 )

Üdvözletem!
Szerettem volna megoldani azt, hogy a főprogramtól elszeparálva, külön fájlban legyen olyan függvény, amit a főprogram használ és külön állományban legyen osztály, amit a főprogramban a külön fájlban definiált függvény használhat. A jelenlegi formában g++ lefordítja jól a forrást és azt is ír ki, amit várok tőle, de gondolom nem túl célszerű egy egész osztálypéldányt függvényparaméterként használni. Gondolom objektumra mutató -val, vagy referenciával lehet ezt elegánsabban megoldani. A hogyannak utána kell olvasnom, mert amit kipróbáltam, az nem működött. Azt szerettem volna, hogy a főprogramban készítek egy osztálypéldányt és az adott osztály típusra mutatót. Majd a mutatónak értékül adom az objektumom címét, és a függvényben paraméterként ezt a mutatót használom. Sajnos ez nem jött össze, mert nem ette meg a g++ az ötletemet. Ezzel később foglalkozom, de előtte azt szeretném, ha páran megszakértenétek a kódot. Bármilyen észrevétel érdekel. Fikázás is, de csak az építő jellegű! Az olyan kérdések, hogy miért nem ezt, vagy azt használtam e-helyett a megoldás helyett, arra az a válaszom, hogy egyelőre ezt a megoldást ismerem és így tudtam elérni a célomat.
Ez lenne a forrás:


fuggveny.h:

#ifndef OSZTALY_H
#define OSZTALY_H
#include "osztaly.h"
#endif

void fuggveny(osztaly);




fuggveny.cpp:

#include <iostream>
#include "fuggveny.h"

using std::endl;
using std::cout;

void fuggveny(osztaly OP)
    {
    cout << "kiir" << endl;
    cout << "szam: " << OP.Get_szam() << endl;
    }



osztaly.h:

class osztaly
{
private:
    int szam;
public:
    osztaly();
    int Get_szam();
};




osztaly.cpp:

#ifndef OSZTALY_H
#define OSZTALY_H
#include "osztaly.h"
#endif

osztaly::osztaly()
    {
    osztaly::szam=6;
    }

int osztaly::Get_szam()
    {
    return osztaly::szam;
    }




proba_main.cpp
#include <fstream>
#include <iostream>
#include <string>
#ifndef OSZTALY_H
#define OSZTALY_H
#include "osztaly.h"
#endif
#include "fuggveny.h"

using std::cout;
using std::string;
using std::cin;
using std::endl;
using std::ifstream;


int main()
{
osztaly Peldany;
fuggveny(Peldany);
}

A fordítás, futtatás és az eredménye:

g++ -c fuggveny.cpp
g++ -c osztaly.cpp
g++ proba_main.cpp osztaly.o fuggveny.o

./a.out
kiir
szam: 6

Köszönöm!

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Én minden headeremet a következőképpen csinálom a többszörös include elkerülésére.
osztaly.h
#ifndef OSZTALY_H
#define OSZTALY_H
erdemleges kod
#endif

fv.h
#ifndef fv_H
#define fv_H
erdemleges kod
#endif

Referencia meg szerintem egyszerű:
Fuggveny(&Peldany);

Igen, _MINDIG_ belső #include őröket használj, sose külsőt!
(Tehát a h file tartalmárt vedd ifndef-ek közé és ne az include-ot.)

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Ez így nem teljesen világos. A fuggveny.h fejállományba kénytelen voltam be- include -olni az osztaly.h fejállományt.

E-helyett:

fuggveny.h:

#ifndef OSZTALY_H
#define OSZTALY_H
#include "osztaly.h"
#endif

void fuggveny(osztaly);

Ezt kellett volna írnom?

fuggveny.h:
#ifndef OSZTALY_H
#define OSZTALY_H
#include "osztaly.h"
void fuggveny(osztaly);
#endif

Vagy ezt?

#ifndef FUGGVENY_H
#define FUGGVENY_H
#ifndef OSZTALY_H
#define OSZTALY_H
#include "osztaly.h"
void fuggveny(osztaly);
#endif
#endif

A .cpp fájlokban szerepelnie kell az #include ... soroknak akkor is, nem?

--
unix -- több, mint kód. filozófia.
Life is feudal

Ezt kellett volna:

fuggveny.h:

#ifndef FUGGVENY_H
#define FUGGVENY_H

#include "osztaly.h"
void fuggveny(...);

#endif

osztaly.h:

#ifndef OSZTALY_H
#define OSZTALY_H
...

#endif

SZVSZ az majdnem mindegy, hogy az #include van e elobb, vagy az #infdef, #define paros.

Szerk:
A referencias megoldast mar irtak felettem (void fuggveny(Osztaly& ojjektum)), de szerintem jobb, ha konstans referenciat passzolsz at a fuggvenynek, mert ha jol latom, nem valtoztatod meg az objektumot: (void fv(const Osztaly& ojjektum)), viszont ekkor az osszes muvelet, amit az ojjektumon vegzel, kotelezoen konstans muvelet kell, hogy legyen.

Jobb az include if -en belul --> forditas 20ns faster .

Nincsen a közelemben C++ fordító, de emlékeim szerint ha referenciaként definiálod a függvény() argumnetumát, akkor elegáns is lesz, meg azt is csinálja majd, amit szeretnél. Azaz nem fog elefántot átadni/venni a stacken, hanem csak egy hivatkozást.

A pointeres turkálás egy fokkal bonyolultabb, különösen az átadott/vett pointer szerinti objektum membereinek a tekergetése során.
Bár az sem halálnehéz. osztály típusú pointernek definiálod az argumentumot, a függvényen belüli osztály member hivatkozáskor meg "." helyett "->"-t használsz.
---------------------------------------------------
Talisker Single Malt Scotch Whisky aged 10 years :)

Elég így:

osztaly::osztaly()
    {
    szam=6;
    }

int osztaly::Get_szam()
    {
    return szam;
    }

Az osztály tagjait az osztályon belül nem kell minősíteni az esetek többségében.
(Kivétel öröklésnél és többszörös öröklésnél lehet, ha több tag rendelkezik azonos névvel, de akkor meg szól a fordító. Ez egyébként is terezési hiba. :) )

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

"Az osztály tagjait az osztályon belül nem kell minősíteni az esetek többségében."
Ez IRL is igy van. :)

Tyrael

C-s (mutatós) megoldás:

fuggveny.h:
#ifndef FV_H
#define FV_H

class osztaly; 
/* Elég ez, ugyanis a fordítónak itt csak azt kell tudni, hogy 
   van egy ilyen osztály, azt nem, hogy mi van benne. Ezzel 
   csökkentettük a függést, így gyorsabb a fordítás, újrafordítás.
*/

void fuggveny(osztaly* );

#endif


fuggveny.cpp:

#include <iostream>
#include "fuggveny.h"
#include "osztaly.h"
/* Itt már kell az osztály deklarációja, hogy a fordító tudja
   milyen tagfüggvények stb. vannak.
*/

using namespace std;
/* Használd ezt, ha nem vagy grafomán, névütközésnél a fordító szól.
   Akkor meg majd külön minősítesz. */

void fuggveny(osztaly* OP)
    {
    cout << "kiir" << endl;
    cout << "szam: " << OP->Get_szam() << endl;
// -> a . helyett, mivel mutatónk van.

    }



osztaly.h:
#ifndef OSZTALY_H
#define OSZTALY_H

class osztaly
{
private:
    int szam;
public:
    osztaly();
    int Get_szam() const;
/*                 ^^^^^^
   Legyen const member fv, ha nem változtatja a fv belső álapotát.
   Ez jó neked, mert a fordító szól, ha mégis rosszalkodsz, és jó a 
   fordítónak, mert felhasználhatja optimalizációra.
*/
};

#endif



osztaly.cpp:

#include "osztaly.h"

osztaly::osztaly()
    {
    szam=6;
// Tagfv-eken belül nem kell minősíteni az osztály tagjait.
    }

int osztaly::Get_szam() const
//  lásd fent           ^^^^^
    {
    return szam;
    }




proba_main.cpp
#include <fstream>
#include <iostream>
#include <string>
#include "osztaly.h"
#include "fuggveny.h"

using namespace std;


int main()
{
osztaly Peldany;
fuggveny(&Peldany);
// & címképző operátor.
}

Illetve a C++-os verzió:
referencia!

fv.h:
void fuggveny(osztaly& );

fv.cpp:
void fuggveny(osztaly& OP)
    {
    cout << "kiir" << endl;
    cout << "szam: " << OP.Get_szam() << endl;
// marad a . 

    }

main.cpp:

int main()
{
osztaly Peldany;
fuggveny(Peldany);
// nem kell semmi...
}

Ez ránézésre egyszerűbb, a fordító persze valójában mutatókat használ, de ez téged ne zavarjon.
Viszont a fuggveny nem módosítja az OP-t, ezt érdemes tudatni a fordítóval:

fv.h:
void fuggveny(const osztaly& );

fv.cpp:
void fuggveny(const osztaly& OP)
    {
    cout << "kiir" << endl;
    cout << "szam: " << OP.Get_szam() << endl;
    }

Ilyenkor OP-nak csak a const fv-eit hívhatjuk. De a Get_szam ilyen.
És ekkor megintcsak tud szólni a fordító, ha véletlenül rosszalkodunk...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

"

lletve a C++-os verzió:
referencia!

fv.h:
void fuggveny(osztaly& );

fv.cpp:
void fuggveny(osztaly& OP)
    {
    cout << "kiir" << endl;
    cout << "szam: " << OP.Get_szam() << endl;
// marad a . 

    }

main.cpp:

int main()
{
osztaly Peldany;
fuggveny(Peldany);
// nem kell semmi...
}

"

A fuggveny.h és fuggveny.cpp állományokat pont így készítettem el, de a főprogramom máshogy néz ki:

osztaly Peldany;
osztaly& PR=Peldany;
fuggveny(PR);

Ezexerint ez:

osztaly& PR=Peldany;

felesleges sor?
--
unix -- több, mint kód. filozófia.
Life is feudal

Igen felesleges.

A referenciára ne úgy gondolj mint egy mutatóra (még ha a fordítóprogram azt is csinál belőle), hanem mint egy álnévre (alias).

Ha egy fv referenciát vár, az lényegében azt jelenti, hogy az objektumot nem másolja le, csak bevezet egy nevet, amin keresztül hivatkozhat rá a fv törzsében.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Tehát attól, hogy a függvény referenciát vár, még meghívhatom objektummal, mint paraméterrel és majd a fordító elsimítja a dolgot, ha jól értem.
Köszönöm a tippet!
Egyébként a mutatós megoldás nem "elegáns" C++ esetében?
--
unix -- több, mint kód. filozófia.
Life is feudal

Mást jelent.

Mutató lehet null, azaz mutathat a "semmire" (és ezt illik ellenőrizni...).
Ezt egyébként C-ben rendszeresen használja az ember, pl a malloc NULL-t ad vissza, ha nem tudott memóriát lefoglalni.

Ezzel szemben a referencia elméletben mindig mutat valahova.


int i;
int* p1;     //int-re mutató mutató, kezdő érték nélkül, rendben
int* p2=0;   //int-re mutató mutató, kezdő érték 0 (NULL), rendben
int* p3=&i;  //int-re mutató mutató, kezdő érték i címe, rendben
int& r1;     //int referencia, nincs kezdőérték, fordítási hiba
int& r2=0;   //int referencia, a konstans 0 álneve, de nem konstans referencia, fordítási hiba
const int& r3=0; //const int ref., a konstans 0 álneve, rendben
int& r4=i;  //int referencia, az i álneve, rendben

++(*p1); //egy ismeretlen memóriacímen lévő int növelés, definiálatlan viselkedés, 
         //jobb esetben futásidejű segfault, rosszabb esetben nehezen megtalálható hiba.
++(*p2); //a semmi növelése, azonnali abort...
++(*p3); // az i növelése, rendben
++r3;    // a 0 növelése, fordítási hiba
++r4;    // i növelése, rendben

Ebből is látszik, hogy a referenciával kevesebb baj van, több minden derül ki fordítási időben, ami jó. C++-ban mindig referenciát kell használni, ha nincs szükség a "semmire mutatok" állapotra.

Arra persze figyelni kell, hogy bár a referencia keletkezésekor mindig érvényes objektumra mutat, ha a mutatott objektum megszűnik, akkor a referencia sem használható. (A mutató esetében ugyanez a probléma).
A legegyszerűbb példa ez:

int& next(const int& a)
{
    int temp=a;
    ++temp;
    return temp;
}

Ilyenkor egy darabig minden jó, "a"-t nem másoljuk, temp-be bekerül "a" értéke (copy constructor), növeljük temp értékét, visszatérünk, temp megszűnik (UPSZ), mi meg visszatérünk egy temp-re mutató referenciával...
Persze ilyet senki nem ír, de ha az int helyére egy nagyobb objektumot képzelsz, amit nem szeretnél másolni, a ++ helyére pedig egy többsoros algoritmust, már nem is olyan feltünő a hiba.
Emlékeim szerint van olyan fordító ami hibát jelez. De van olyan eset ahol a dolog nem ennyire triviális...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Mintha a Gyakorlati C++ könyvben is benne láttam volna ezt a problémát...
Azért köszi!
--
unix -- több, mint kód. filozófia.
Life is feudal

Köszönöm a segítségeket!
Most megpróbálom megérteni amit írtatok és ha sikerült, akkor jövök a következő kérdésemmel :D
--
unix -- több, mint kód. filozófia.
Life is feudal

http://hup.hu/node/46202#comment-446908
Most egy kicsit elbizonytalanodtam. Itt a topikban nem odáig jutottunk el, hogy konstans referencia legyen a függvényparaméter? Miért nem ajánlott dolog ez C++ esetén?
--
unix -- több, mint kód. filozófia.
Life is feudal

hogy-hogy.hogy nem ajánlott? Ez az ajánlott, ha nem szabad megváltoztatni a paraméter értékét és nem érdekel, hogy pointer vagy nem pointer volt eredetileg az átadott változó.

Teljesen bevett szokas c++ban konstans referenciakent atadni a fuggveny parametereit, ha azokat nem akarod megvaltoztatni a fuggvenyen belul.

FYI:
Ketfele "konstans referencia" letezhet.
const& Foo es const Foo&
Az elso redundans (es a fordito sir ilyenkor), hiszen a referencia mindig konstans abban az ertelemben, hogy nem lehet megvaltoztatni, hogy melyik objektumra referal, tehat a "konstans referencia" alatt igazabol a "konstansra mutato referenciat" szoktuk erteni

A linkelt hozzászólásban is az van, hogy használj konst referenciát.
Hol az ellentmondás?

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Elnézést! Tényleg. AKkor álmos voltam tegnap.
--
unix -- több, mint kód. filozófia.
Life is feudal

Lehet, hogy volt (nincs kedvem vegigolvasni a teljes beszelgetest), de a kovetkezo szabalyt erdemes alkalmazni (lagalabbis mi ezt alkalmazzuk):

Ha referencia, akkor nem veszed at a tulajdonossagot. Ha mutato, akkor "megoroklod" az objektumot, azaz te felelsz erte. Ertek szerint csak nagyon ritkan adunk at komplex adattipust. Es ez a legtobbszor felesleges, hiszen a referencia szintaktika szempontjabol hasonloan viselkedik.

"i pensieri stretti & il viso sciolto." -- Sir Henry Wootton