31. oktober 2001 - 19:14Der er
51 kommentarer og 1 løsning
flere members= ulovlig handling, hvorfor dog?
Jeg er igang med at lave et Windows-program i C++, og jeg har et meget mærkeligt problem, som blokere min videre udvikling af programmet en hel del: Jeg kan ikke oprette members, uden at mit program udfører en ulovlig handling eller i det hele taget opfører sig underligt. Jeg tror ikke det kan være hukommelsesproblemer, for programmet er overhovedet ikke stort endnu. Der skal ikke mere end en bette char til. Det virker fint, hvis jeg gør samme variabel global i stedet for. Er der nogen, der kan hjælpe mig, for det driver mig til vanvid!!!
Er det eneste du gør, at tilføje variablen som en member, altså UDEN at bruge den? Hvis ja, så prøv at slette alle kompilergenererede filer og genbyg skidtet...
Det virkede desværre ikke, jpk. Noget kode? Mmh, det kunne godt gå hen og blive stort, men jeg kan lige vise princippet i det: class Klasse { ... }; virker men så: class Klasse { ... private: int _i; }; og så virker det lige pludselig ikke mere. Det var nok ikke til meget hjælp. I skriver bare igen, hvis I vil have noget rigtigt kode, så kan jeg evt. maile det.
til disky: hvordan jeg instatierer den? øh... Klasse nyKlasse; ...hvad ellers. Men det er altså kun et eksempel. Jeg tror hellere, jeg må sende koden. Kan I ikke give mig jeres email-adresser.
til jpk: Jeg bruger Borlands gratis compiler (jeg HAR installeret de nyeste patches) og programmerings-teksteditoren ConTEXT (men editoren skulle vel ikke kunne gøre en forskel)
jeg kan ikke umiddelbart se, hvordan din kode skulle hjælpe mig til at forstå problemet. Jeg kører selvfølgelig også efter de almindelig principper (det kan vel ikke gøres anderledes), men sagen er den i dette tilfælde, at det opfører sig ekstremt underligt.
Så må du s.. være mere specifik. Du lægger en lille stump ud, som ingen af os kan finde noget galt i... ALTSÅ må det være noget andet i din kode. Eksempler som d.o. er tænkt som hjælp til dig til msåke selv at opdage hvad der er galt.
Du skriver bare at \'og så virker det lige pludselig ikke mere\' - det er dælme godt nok ikke en særlig præcis angivelse af dit problem.
Husk: Som man spørger, således for man svar !
(Ikke noget jeg har fundet på, men vist sagt af Søren Kierkegaard.)
undskyld forsinkelsen, men nettet gik lige ned. her er noget af den (der er altså en hulens masse, hvis jeg skal have det hele med, men I siger bare til, så kommer jeg også med den). I skal ikke tage jer af den udkommenterede kode. Det er bare fordi det stadig er på forsøgsplanet, og der er ting og sager som skal slås fra og sådan.
class Control { protected: Control ( DowMaker & windowMaker, Controller & controller, char * text, int x, int y, int width, int height, char * type ) : _windowMaker (windowMaker), _controller (controller), _text (text), //_x (x), _y (y), _width (width), _height (height), _type (type) {}
class ButtonController : public Controller { public: virtual bool OnClick () { return false; } };
class Button : public Control { public: Button ( DowMaker & windowMaker, ButtonController & controller, char * text, int x, int y, int width, int height ) : Control ( windowMaker, controller, text, x, y, width, height, \"BUTTON\" ) {} };
}
#pragma warn +8026 // Functions with exception specifications are not expanded inline
jeg beklager meget, at jeg ikke er mere specifik med mit problem, men det jeg ved altså ikke hvad jeg skal trække ud, når jeg ikke kan forklare hvorfor det går ned. respekt til Søren Kirkegaard :)
forresten: den udkommenterede linje i objektet control (//int _x, _y, _width, _height;) er også en linje, som får skidtet til at gå ned. Her får jeg et NULL fra CreateWindowEx (hvis ikke jeg får en ulovlig handling), men GetLastError () viser ikke nogen fejl. Dow og DowMaker er inkapslinger af CreateWindowEx og HWND.
hvis de er de der underscores før navnene, du mener, er det bare en måde, jeg navngiver private members på. Så kan man hurtigere skeldne fra andre variabler.
template <class T> inline T GetLong (HWND hwnd, int which = GWL_USERDATA) { return reinterpret_cast <T> (::GetWindowLong (hwnd, which)); }
template <class T> inline T SetLong (HWND hwnd, T value, int which = GWL_USERDATA) { return reinterpret_cast <T> (::SetWindowLong (hwnd, which, reinterpret_cast <long> (value))); }
/* We can\'t include Controller.h before declaration of Win::Dow because Win::Controller has a Win::Dow member, wich requires a implementation of Win::Dow before the implementation of Win::Controller. A reference requires declaration only. */ class Controller;
operator HWND () { return _h; } private: // Parametres passed to CreateWindowEx: DWORD _exwStyle; // extended window style LPCTSTR _className; // pointer to registered class name LPCTSTR _windowName; // pointer to window name DWORD _wStyle; // window style int _x; // horizontal position of window int _y; // vertical position of window int _width; // window width int _height; // window height HWND _hWndParent; // handle to parent or owner window HMENU _hMenu; // handle to menu, or child-window identifier HINSTANCE _hInst; // handle to application instance LPVOID _data; // pointer to window-creation data // the window-handle: HWND _h; }; }
De mange kald til MessageBox viser bare, hvordan jeg har fundet det sted, den går ned. Den når til msgbox 8. Dvs. det er kaldet til std::strcpy i SetDefaults der får den til at gå ned. Men hvad i alverden har et kald til strcpy at gøre med en tilfældig variabel i en anden klasse???
Nu har jeg fjernet strcpy kaldet (den var alligevel ikke nødvendig), og nu virker det. MEN: Jeg udlover stadig pointene til den, der kan sige mig, hvorfor det gik ned af kaldet til strcpy, for jeg kunne jo gå hen og få brug for det igen.
Svaret er ret simpelt: Du kalder strcpy(\"New window\", _windowName) med \"New window\" som dest-parameter, men \"New window\" er readonly-memory og ikke en variabel. Du overskriver altså noget tilfældig memory i dit program så det går ned.
Koden skulle istedet have været: strcpy(_windowName, \"New window\");
Den køber jeg ikke helt, joha. Jeg ved godt, at destinationen normalt står først i strcpy, men det synes min compiler ikke i dette tilfælde. Under alle omstændigheder bliver en alm. streng i appostroffer tolket af compileren som const char * og destinationen er selvfølgelig erklæret som char *. Hvis du beder om en char * og giver en const char * brokker compileren sig højlydt, så du kommer aldrig så langt, at det kan gå ned på den måde.
næh, undskyld, _windowName er jo ærklæret som const char *, så det var det, den var sur over. \"blabla\" er åbenbart ikke en const char * alligevel. Sorry!! Hvor dum har man lov til at være!! Men: Nu har jeg prøvet at erklære _windowName som char * istedet, og at skrive parametrene i strcpy i rigtig rækkefølge, og det går stadig lige så lystigt ned.
mmh, det kunne godt være, det skulle laves om. Det har ikke forvoldt problemer indtil nu, men jeg kan godt se din pointe. Nu må man håbe, det ikke begynder at gå ned. Vender tilbage, når det er rettet...
Okay, jeg har gjort følgende: char * _windowName; i stedet for LPCTSTR _windowName; Og: std::strcpy (_windowName, title); i stedet for _windowName = title;
i begge har du defineret en variabel som en pointer til chars, men du har ikke reserveret noget hukommelsesområde, som de skal pege på. Det betyder at _windowName peger på et tilfældigt område i hukommelsen. Når du så laver denne her:
std::strcpy (_windowName, title);
så forsøger du at skrive data fra title til et tilfældig sted i hukommelsen (hvad _windowName nu tilfældigvis peger på). DET MÅ MAN IKKE!
Jeg tror, jeg har fundet ud af det: Hvis jeg allokerer hukommelsen før kaldet til strcpy, går det ikke ned. Altså I Create: _windowName = new char [std::strlen (title)]; std::strcpy (_windowName, title); Og selvfølgelig en destruktor til at deallokere: ~DowMaker () { delete _windowName; } Og en ny linje i SetDefaults, så vi ikke prøver at slette en NULL-pointer: _windowName = new char;
Jeg troede egentlig, at strcpy selv fandt ud af at allokere. Skulle det ikke det?
Jeg tænkte lige på denne linje i constructoren: DowMaker (char const * className, HINSTANCE hInst) : _className (className), Er det noget gris. På den ene side: da den streng, der peges på, er nødt til at eksistere i det samme scope, som constructoren bliver kaldet i, gør det ikke noget. Men på den anden side: char * streng = new char; DowMaker maker (streng, hInst); delete streng; DowMaker.Create (); Da streng bliver brugt i Create, går det vel galt. Hvis det er noget gris, hvad gør man så, hvis man har en char * const eller eller char &. De SKAL jo indlæses i constructoren.
Nej strcpy() finder IKKE selv ud af sådan noget - det er der faktisk ingen \"hukommelsesflytter\" funktioner, der gør. Du har ALTID ansvaret for at allokere og deallokere hukommelse i C/C++
Det ansporer også et vist krav til selvdisciplin. Det er så nemt at lave en funktion, der allokerer noget hukommelse og returnerer en pointer til denne (en nem måde at returnere komplekse variable på), hvorefter det er den kaldende funktions opgave at frigive hukommelsen. DET MÅ DU ALDRIG GØRE! Du hænger dig selv ganske langsomt.
Regel nr. 1: Hukommelse skal altid frigives i samme \"scope\", som det blev allokeret. Dvs. indenfor samme loop, funktion, objekt (constructor/destructor), etc.
Regel nr. 2: Hukommelse skal altid frigives i samme \"scope\", som det blev allokeret. Dvs. indenfor samme loop, funktion, objekt (constructor/destructor), etc.
Regel nr. 3: Hukommelse skal altid frigives i samme \"scope\", som det blev allokeret. Dvs. indenfor samme loop, funktion, objekt (constructor/destructor), etc.
Hmm... Det er lavet i BC? Den har jeg desværre ikke, og dermed forsvinder min mulighed for at \"pille mig frem\" til resultatet...
Jeg synes, at du skal prøve at debugge dig igennem koden trin for trin. I den hvor programmet dør, undersøger du, om den/de pointere, du benytter, er oprettet med ovenstående regler i mente. Så skulle du meget gerne finde fejlen.
_windowName = new char; skal være _windowName = new char[200]; ellers laver du blot en pointer til en enkelt char, og det er jo ikke nok :)
_windowName = new char [std::strlen (title)]; skal være _windowName = new char [std::strlen (title) + 1]; ellers er der ikke plads til null-termineringen
OK til den sidste, men den første kan være ligemeget. Den allokerer jeg bare, så jeg ikke kommer til at prøve at delete en NULL-pointer i destruktoren, hvis objektet slettes uden at have kaldet Create. Jeg skal forresten huske at delete den lige før jeg allokerer i Create.
Du kan hente BC gratis på borlands hjemmeside, hvis du vil.
~DowMaker () { if (_windowName) delete _windowName; }
Så behøver du ikke allokere hukommelse til _windowName, før du skal bruge den. (Hvilket også var noget rod. Du Frigiver jo ikke den hukommelse, før du allokerer nyt til _windowName)
Jeg tror jeg vil gøre det på en anden måde. Man kan godt kalde Create flere gange, så der skal alligevel frigøres før det nye kald til strcpy. Det skal altså se sådan ud: ~DowMaker () { delete _windowName; }
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.