Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++ muuttujien tilanvaraus ja välittäminen toisille luokille

Sivun loppuun

tomppapa [22.07.2006 12:09:12]

#

Kirjoitan ohjelmaa Borlandin c++ builderilla ja muuttujia käsitellessäni edessä on ongelma. Ohjelma koostuu useammasta luokasta. Käytössä on iso kasa muuttujia jotka syötetään lomakkeelta. Sitten niitä pitää välittää eri luokille, jotka käyttävät niitä. Kyseessä on lukuja, luku taulukoita ja merkkijono taulukoita. Lomakkeisiin, joihin tiedot syötetään olen tehnyt "event":sit, jotta muuttujat tallentuvat muistiin. Tässä vaiheessa siis ei tarvita muistinvarausta(??), koska olen saanut ne muuttujiin muistiin. Mutta sitten kun yritän välittää niitä muille luokille saan ajonaikana tälläisen virheilmoituksen:


Project xxx raised exception class EAccessViolation with message 'Access violation at address 00415668. Write of address 00000000'. Prosess stopped.

Alla koodipätkä johon virhe osoittaa. Muuttujaesittelyssä tässä luokassa on "int *SR;" ja Main:ssa "int SR;".

SR = new int;
SR = &Main->SR;

Jotain siis pahasti pielessä!

Smuliz [22.07.2006 12:13:38]

#

Kokeile vaihtaa

SR = new int;    // tämä
SR = new int[1]; // näin

tomppapa [22.07.2006 12:41:26]

#

Ihan samallai valittaa kun tein noin.

Metabolix [22.07.2006 16:13:38]

#

int i;
int * p; // Osoitin int-muuttujaan (tai useampaan)

p = new int;
*p = 10; // Huomaa, että itse kokonaislukuun pääsee käsiksi *:llä.
cout << p[0]; // Hakasuluissa nolla toimii myös
delete p; // Se pitää tuhotakin, ettei tule muistivuotoa

p = new int[10]; // Uusi taulukollinen (0..9)
for (i = 0; i < 10; ++i) {
  p[i] = 10 * i; // Hakasulut toimivat näin tällä kertaa
}
delete [] p; // Ja taulukollisen tuhoaminen

p = &i; // p osoittamaan i-muuttujaan
*p = 10; // *p = 10 eli i = 10
cout << *p << " == " << i << endl;

Muista varoa muistivuotoja. Kuten huomaat, viimeisessä kohdassa ei lainkaan varata ylimääräistä tilaa, vaan asetetaan muuttuja osoittamaan jo olemassaolevaan mmuuttujaan.

Tuo virheilmoituksesi kertoo, että yrität kirjoittaa muistiin osoitteessa nolla eli että sinulla on jossakin osoitin, jonka arvo on NULL eli nolla ja jonka osoittamaan kohtaan yrität kirjoittaa.

CyberianRat [22.07.2006 16:25:30]

#

int * SR; /* SR on osoitin */
SR = new int; /* SR osoittaa juuri varattuun muistiin */
SR = &Main->SR; /* SR osoittaa jonnekin muualle, kuitenkin varattu muisti pitäisi vapauttaa myöhemmin */

Tuo on ainakin vikana. Tarkista myös onko *Main alustettu.

tomppapa [24.07.2006 20:04:21]

#

Mitäs vikaa tässä on kun edelleen samaa valittaa.
SR esitelty int *SR;

void TVAL::setSR(int tmp) {
    SR = new int;
    *SR = tmp;
}

Metabolix [24.07.2006 20:31:31]

#

No ei tuossa olekaan (paitsi että joka kerta luot uuden intin, jolloin olet saanut taas aikaan muistivuodon). Oletettavasti olet luonut sen TVAL-tyyppisen muuttujan?

tomppapa [24.07.2006 22:32:23]

#

Kun ei tunnu ratkeavan niin laitan tänne kaiken koodin jonka oletan liittyvän ongelmaan.

Eli Main-luokka sisältää graafiset komponentit jossa muuttujaan SR tallennetaan luku. Sitten lukua pitäisi myös käyttää muissa luokissa - tässä tapausessa luokassa VAL.

Main.h

...
class TMain : public TForm {
...
public:
	int SR;
...
}

Main.cpp

...
VAL->start();
...

VAL.h

...
class TVAL {

private:
	int SR;

public:
        void start();
        int getSR();
        void setSR(int);

VAL.cpp

...
void TVAL::start() {
	VAL->setSR(Main->SR);
...
	int luku = VAL->getSR();
}

int TVAL::getSR() {
        return SR;
}

void TVAL::setSR(int tmp) {
        SR = tmp;
}

koo [24.07.2006 23:24:04]

#

Kammoksun kauhiasti ohjelmointityyliä, jossa kaiken maailman asiat luodaan new:lla sen sijaan, että ne olisivat ihan yksinkertaisia paikallisia muuttujia tai edes std::auto_ptr:iin käärittyjä pointtereita.

No, joka tapauksessa, koska pikaisella silmäilyllä en tuosta näytekoodista äkkää varsinaista vikaa (hienoisia outouksia kylläkin), arvelen pulman olevan koodissa, joka ei ole näkyvillä.

Mitenkäs tuo VAL-muuttuja on siellä Main.cpp:ssä alustettu? Siis, mitä (globaalille?) VAL-muuttujalle tehdään, ennen kuin sanotaan että VAL->start()?

tomppapa [24.07.2006 23:49:23]

#

Ei ole alustettu missään Main.cpp:ssä. Main.cpp:ssä on rivi #include "VAL.h".

Ongelma on siinä, että Javalla koodanneena en osaa välttämättä kunnolla edes ideaa miten rakennan luokat ja välitän muuttujia niiden välillä...

Käytössä on vanha c++ koodi joka koostuu useasta luokasta. Rakensin uuden käyttöliittymän ohjelmalle ja lomakkeilta syötetyt muuttujat pitäisi saada välitettyä muille, vanhoille, luokille.

koo [25.07.2006 08:34:49]

#

Muuttuja täytyy alustaa ennen käyttöä. Jossain pitäisi siis lukea, että

TVAL *VAL = new TVAL;

tai vaikka

TVAL VAL;

jolloin ainakin kaksi riviä esimerkissä pitää muuttaa (VAL.cpp)

void TVAL::start() {
        VAL.setSR(Main->SR);
        // ...
        int luku = VAL.getSR();
}

Globaaleja muuttujia ei kannata suosia. Muuttujat voisivat olla vaikkapa pääohjelman rungossa esiteltyjä ja niitä voisi välittää viittauksina muille funktioille.

tomppapa [25.07.2006 11:37:51]

#

Teen ensin lyhyemmin ilman new:tä.

TMain olio;
TVAL olio2;

Valittaa:

  [C++ Error] Main.cpp(257): E2459 VCL style classes must be constructed using operator new
  [C++ Error] Main.cpp(257): E2285 Could not find a match for 'TMain::TMain()'

Sitten teen ohjeen mukaan seuraavasti

        TMain *olio = new TMain();
        TVAL *olio2 = new TVAL();

ja valittaa:

  [C++ Error] Main.cpp(257): E2285 Could not find a match for 'TMain::TMain()'

Eli pitäisi tehdä jokin funktio? Konstruktori? Millainen?

koo [25.07.2006 13:30:03]

#

Luulen, että sinun kannattaisi perehtyä vähän tarkemmin ohjelmoinnin ja ohjelmointikiel(t)en perusteisiin ja hypätä tapahtumapohjaiseen käyttöliittymäohjelmointiin vasta hieman myöhemmin. Tie voi olla joka tapauksessa pitkä, muttei ehkä ihan niin kivikkoinen.

En tunne noita Borlandin GUI-kirjastoja oikeastaan yhtään. Arvaan, että TForm-luokalla ei ole oletuskonstruktoria (siis sellaista, jolle ei tarvitse antaa mitään parametria), vaan kaikki (ainoa?) konstruktorit haluavat jonkinlaisen parametrin. Tällainen parametri voisi arvattavasti olla vaikkapa viittaus johonkin GUI-komponenttiin, jonka olisi tarkoitus "omistaa" tai olla "isänä" luotavalle formille. TMain-luokka on johdettu TFormista. TMainin on konstruktorinsa alustuslistassa annettava TFormin konstruktorissaan kaipaama parametri.

Vaikka virheilmoitus urputtaa TMain-luokasta, virhe juontuu siis TForm-kantaluokan käytöstä.

Arvailujeni mukaan homman pitäisi siis toimia jotenkin näin:

class TMain : public TForm {
public:
        int SR; // kannattaisi ehkä olla private
        // sitten näitä vaihtoehtoja
        TMain::TMain() : TForm(0), SR(0) {}
        explicit TMain::TMain(TWhatever *parent) : TForm(parent), SR(0) {}
        // ...
};

Kaikkea koodia ei tokikaan kannata/tarvitse kirjoittaa tuohon luokan esittelyyn, vaan sinne varsinaiseen toteutustiedostoon. Tässä se nyt on vain esimerkin ja lyhyyden vuoksi näin.

Muuten, konstruktori on ihan oiva paikka hoitaa jäsenmuuttujien - kuten tuon SRn - alustus.


Sivun alkuun

Vastaus

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

Tietoa sivustosta