Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Olion kopiointi, jäsenenä dynaaminen taulukko

Sivun loppuun

kayttaja-3842 [16.05.2013 19:55:03]

#

Tervehdys kaikki,

Olen tässä jonkin aikaa pähkäilly kuinka saisin tehtyä luokan johon voin määrittää x kokoisen taulukon ja luokkaa ei tarvitsisi luoda new -operaattorilla.

Eli siis taulukko1 pitäisi saada _fArray sisältö kopioitua taulukko2. Nythän on se ongelma, että taulukko2 = taulukko1; kopioi myös taulukko1 _fArray pointterin osoitteen taulukko2 seen.

class DynTaulukko
{

public:
 DynTaulukko( int koko 4){ _fArray = new float[koko];  };
 ~DynTaulukko(){ delete [] _fArray; }

public:
 void setValue( int iIndex_, float fValue_ ){ _fArray[iIndex_] = fValue_; };

private:
 float *_fArray;

};

DynTaulukko taulukko1;
taulukko1.setValue( 0, 20.0 );

DynTaulukko taulukko2;

taulukko2 = taulukko1;

Sampe [16.05.2013 20:17:20]

#

Voit määrittää luokalle itse sijoitusoperaattorin sekä kopiorakentajan toteutuksen. Tällöin voit kopioida _fArray:n uuteen olioon.

// Kopiorakentaja
MyClass( const MyClass& other );

// Sijoitusoperaattori
MyClass& MyClass::operator=( const MyClass& other );

Noin yksinkertaisessa koodissa suosittelisin käyttämään esim. vektoria tai muita standardikirjaston säiliöitä dynaamisen muistinvarauksen sijaan.

Metabolix [16.05.2013 20:22:38]

#

Kaikkein helpointa, turvallisinta ja luultavasti myös tehokkainta olisi tosiaan käyttää valmista luokkaa std::vector. Omat varaukset menevät todella usein pieleen, ja niiden suunnittelu ja kirjoittaminen on myös erittäin vaivalloista verrattuna valmiin luokan käyttöön.

User137 [16.05.2013 20:52:39]

#

En tiedä onko viisasta ylikuormittaa sijoitus-operaattoria luokkien yhteydessä. Monta kertaa osoittimen kopioimisestakin on hyötyä. Mieluummin tekee vaikka Clone() funktion joka kopioi sisällön uuteen olioon.

kayttaja-3842 [16.05.2013 20:52:50]

#

Hmm. Mites ajattelit noilla luoda tuollaseen?

Kopiorakentajalla tulee ongelmaksi jälleen ettei vanhaa ( taulukko2 ) pointteria saa haettua vaan joudun luomaan uuden float arrayn taulukko1 tiedon pohjalta.

Aikalailla sama ongelma = -operaattorilla.

...std::vectoria käytän ohjelmassani monessakin paikkaa. Totesin vain, että ko. luokkaan en halua sitä tunkea. Kyseessä on siis Matrix -luokka, joka periytetään Matrix4 luokkaan.

// Base -luokka
class Matrix( int iRows_, int iColumns_ ){}

// Voin luoda monta erilaista matrix luokkaa.
class Matrix4 : public Matrix( 4, 4 ){};
class Matrix3 : public Matrix( 3, 3 ){};
class Matrix2 : public Matrix( 2, 2 ){};

Edit:

Lisäksi ongelmaksi muodostuu sama myös vectoreilla.

class Luokka
{

private:
 // Tämä ei ole käsittääkseni sallittua, jos kyseessä on dll
 vector<int> _vVector;
 // Tämä on sallittu, mutta ongelmaksi muodostuu jälleen pointteri.
 vector<int> *_vVector;

};

Metabolix [16.05.2013 21:31:35]

#

Voit kyllä käyttää DLL-tiedostossa vector-luokkaa.

Jos käännät DLL-tiedoston ja ohjelman samalla kääntäjällä (samalla versiolla, samolla standardikirjastolla), kaiken koodin pitäisi toimia aivan normaalisti. Silloin DLL-tiedoston ja ohjelman käsitys vector-luokasta on sama, joten molempien operaatiot toimivat oikein.

Jos käännökset tapahtuvat eri kääntäjillä tai esimerkiksi levität pelkkää DLL-tiedostoa netissä muille, sinun pitää ajatella, että DLL-tiedoston vector-luokka on eri asia kuin sovelluksen vector-luokka. Silloin DLL-tiedoston näkyvässä rajapinnassa ei saa esiintyä vector-luokkaa, koska ohjelma ei pysty käsittelemään sitä. Voit kuitenkin aina tehdä koodia, jossa vector-luokkaa käytetään vain DLL:n sisällä. Siihen voit käyttää abstraktia kantaluokkaa ja virtuaalisia metodeja.

// DLL:n julkinen rajapinta ei sisällä vector-luokkaa.
struct Rajapinta {
	static Rajapinta* create();
	virtual int get(int i) = 0;
};
// DLL:n sisäinen koodi voi sisältää mitä tahansa.
// Tämän koodin ei pidä näkyä ohjelmalle!
struct Toteutus: Rajapinta {
	std::vector data;
	virtual int get(int i) {
		return data[i];
	}
};

// Olio luodaan erillisellä funktiolla.
Rajapinta* Rajapinta::create() {
	return new Toteutus;
}

Toisaalta kannattaa miettiä myös, miksi edes teet DLL-tiedostoa.

jlaire [16.05.2013 21:35:52]

#

User137 kirjoitti:

En tiedä onko viisasta ylikuormittaa sijoitus-operaattoria luokkien yhteydessä.

Ei siinä ole mitään vikaa, päinvastoin se on idiomaattista C++:aa.

User137 kirjoitti:

Monta kertaa osoittimen kopioimisestakin on hyötyä.

Tähän soveltuu esimerkiksi shared_ptr<vector<T>>. Tavallisen osoittimen kopiointi kuulostaa todella vaaralliselta.

User137 kirjoitti:

Mieluummin tekee vaikka Clone() funktion joka kopioi sisällön uuteen olioon.

Ei kannata turhaan kehitellä omia viritelmiä.

User137 [16.05.2013 23:57:27]

#

Otetaan sitten vaikka joku käytännön peli-esimerkki.
- Taulukollinen olioita AmmusTyyppi.
- Asetyyppi-luokassa viittaus yhteen AmmusTyyppi-olioon.

Voithan sen tietysti tehdä numeroindeksinäkin taulukkoon, mutta on paljon kätevämpää viitata suoraan itse olioon. Ehkä jopa tehokkaampaa. Pitää vaan tietää että vapautukset tehdään vain taulukosta suoraan, ei muista viittauksista.

Jos tuo olisi taulukollinen osoittimia, niin vielä suurempi etu. Ei tarvitse alkaa vaihtamaan indeksejä joka ikisestä siihen viittaavasta oliosta, jos taulukkoa muutetaan, tai välistä poistetaan jotain.

Clone on ihan yhtä lailla oma viritelmä kuin operaattorin ylikuormitus. En minä mistään hatusta ideoitani heitä
http://answers.yahoo.com/question/index?qid­=20110215080436AAQD3TD

Jouko Koski [17.05.2013 10:41:14]

#

kayttaja-3842 kirjoitti:

kuinka saisin tehtyä luokan johon voin määrittää x kokoisen taulukon ja luokkaa ei tarvitsisi luoda new -operaattorilla.

Muutkin jo kertoivat, mutta sanotaan vielä: Älä tee. Käytä std::vectoria.

kayttaja-3842 kirjoitti:

Kyseessä on siis Matrix -luokka, joka periytetään Matrix4 luokkaan.

Tee matrix-luokkakaavain. Älä käytä periyttämistä.

template<size_t R, size_t C> struct matrix {
    float x[R][C];
    float &operator(size_t r, size_t c) { return x[r][c]; }
    float const &operator(size_t r, size_t c) const { return x[r][c]; }
};

typedef matrix<4, 4> matrix4;
typedef matrix<3, 3> matrix3;
typedef matrix<2, 2> matrix2;

Noin siis yksinkertaisimmillaan. Esimerkkejä on helppo löytää, koodivinkkejä näyttää olevan täällä Putkassakin.

User137 kirjoitti:

Clone on ihan yhtä lailla oma viritelmä kuin operaattorin ylikuormitus.

Kloonausta käytetään vain periyttämiseen kanssa, jolloin sijoitusoperaattoria ei saa käyttää.

Sampe [20.05.2013 10:01:20]

#

Jos kopiorakentajaa tai sijoitusoperaattoria ei haluta luokalle määritellä, kannattaa ne kuitenkin esitellä luokan private-puolella. Jos kopiorakentajaa tai sijoitusoperaattoria ei määritellä lainkaan, generoi kääntäjä niille automaattisesti oletustoteutuksen (kopioinnissa matalakopiointi, sijoituksessa kaikki jäsenmuuttujat yksinkertaisesti sijoitetaan järjestyksessä).

Kun kopiointi ja sijoitus on esitelty private-puolella, tulee käännösvirhe, jos niitä yritetään käyttää. Esim. kopiorakentajaa tulee melko helposti käytettyä vahingossa, jos luokan olio välitetään arvoparametrina viitteen tai osoittimen sijaan.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta