[C++] Singletony i brak obiektu this...

0

Witam.

Po 2 dniowej walce stwierdzam, iż się poddaje...

Mam 3 pliki (właściwie w projekcie jest ich z 30, ale zajme się opisywaniem tych trzech).

Singleton.h
Objects.cpp
Objects.h

Kod źródłowy Objects.h

#define ADDRESS 0x784D2C

#ifndef __OBJECTS_H
#define __OBJECTS_H

enum ObjectFlags
{
	HasHelpLens = 0x20000000,
	IsGround = 0x40000000
};

enum ObjectLensHelp 
{
    HasSpecialDescription = 0x457,
    IsReadOnly = 0x458
};

class ObjectProperties
{

  public:

    int width;
    int height; 


	ObjectFlags flags;
        ObjectLensHelp lensHelp; 
		
	bool hasFlag(ObjectFlags flag)
	{
		return (flags & flag) ? true : false;
	}

};

class Objects
{

  public:
	
	// Konstruktor / Destruktor
	Objects();
	~Objects();

	ObjectProperties* getObjectById(UINT16 itemid);

	int heapStart;

};

#endif

Kod źródłowy Objects.cpp

#include "stdafx.h"
#include "objects.h"

extern void debug(const char* napis);
extern void debug(int liczba);

Objects::Objects()
{
	if(!Singleton::objects)
	Singleton::objects = this;

	int output = (*(int*)(ADDRESS)) + 0x08;
	
	this->heapStart = *(int*)(output);

	debug("heapStart z konstruktora");
	debug(heapStart);
}

Objects::~Objects()
{
	Singleton::objects = NULL;
}

ObjectProperties* Objects::getObjectById(UINT16 itemid)
{
	debug("Wskazniki");
	debug((int)this); // W debugowym pliku drukuje 0... (dlaczego?)
	debug((int)objects);

	ObjectProperties* object = new ObjectProperties;

        // No i tutaj następuje crash... z powodu odwołania się do heapStart
	object->width = heapStart;
	object->height = heapStart;
	
        debug("Tego już nie wyświetli bo nastąpil crash programu");

	return object;
}

Kod źródłowy Singleton.h

#ifndef __SINGLETON_H
#define __SINGLETON_H

class Objects;
class Protocol;

namespace Singleton
{

	static Objects* objects = NULL;
	static Protocol* protocol = NULL;
};

#endif

Problem w krokach opisalem także w komentarzach w kodzie źródłowym.
Wyjaśnię jeszcze raz:

po prostu w funkcji getObjectById nie ma dostępu do wskaźnika this...
Ma on wartość 0.

Jeżeli bym w funkcji getObjectById odwoływał się do Singleton::objects to wszystko jest ok.

Jeżeli ktoś by dał rade pomóc to bym był bardzo wdzięczny.

Pozdrawiam.

0

dziwne rzeczy probujesz robic w konstruktorze klasy Objects, ale moze nie bede wnikal skoro nic o tym nie mowisz

do rzeczy:

  • w kazdej jednej niestatycznej dowolnej metodzie masz zagwarantowany dostęp do wskanzika this. sam fakt ze kompilator przyjmuje taki kod swiadczy ze dostep masz.
  • to ze this == 0 nie oznacza ze 'nie masz dostepu', to jedynie oznacza, ze this jest rowny zero (:)), czyli ze ten obiekt jest umiejscowiony fizycznie "nigdzie"
  • a to z kolei oznacza, ze to cos co wywolalo te metode jest najprawdopodobniej bledne. to cos wzielo sobie skads wskaznik=0, zalozylo sobie ze na pewno nie jest zerowy bo jakim prawem by byc mial taki, i na nim probowalo wywolac tę metodę. powtarzam: cos wywolalo te metode na nullpointerze. ot, tak po prostu *)

dopoki nie pokazesz kodu prowadzacego do wywolania getObjectById, nikt Ci wiecej obawiam sie ze nie powie. zbadaj tamten kod. blad jest gdzies tam.

*) jest pare przypadkow w ktorych operacje na metodach wirtualnych obiektow moga spowodowac odpalenie metody z this==0 nawet jesli obiekt zostal stworzony prawidlowo. ale u Ciebie to raczej nie ten przypadek - nie masz nawet metod wirt.

ps. a teraz zabawie sie we wrozke:

  • jestes pewien ze w miejscu w ktorym uzywaz getObjectById poprawnie odbierasz wartosc/obiekt tego singletonu?
  • jestes pewien ze inicjalizacja singletonu zdazyla sie wykonac?
  • [zakladam ze nie uwazasz po newbiemu, ze skoro ctor Object inicjalizuje singleton, to odtad wartosc singletonu magicznie pojawi sie w kazdym object*]
  • fajnie ze masz singleton, ale inicjalizujesz go przedziwnie. czemu z wewnatrz konstruktora?? nie widac zadnych, minimalnych nawet zabezpieczen przed stworzeniem sobie trzeciego czy piecdziesiatego kolejnego obiektu, tyle tylko ze zostanie on zgubiony ew. usuniety. razem z zawartoscia 'singletona', patrz ~Objects
0

Wywołanie metody wyglądało tak:

(odwołuje się Od razu do objects bez "Singleton::" bo wczesniej dopisałem using namespace Singleton; )

(wywołanie jest w obiekcie Protocol)

ObjectProperties* properties = objects->getObjectById(id);

a tutaj w funkcji main tworzenie tych singletonów

void main()
{
	new Objects();
	new Protocol();
	
	while(true)
	{
	     // cos sie dzieje
	}

	delete Singleton::protocol;
	delete Singleton::objects;
}

Dodam, że jest to aplikacja wielowątkowa i to wszystko jest w bibliotece dll ( funkcja main to tylko taka zmyła, a naprawde ona jest wywoływania w DllMain ) cały projekt ma kilka tyś linijek kodu. więc mogłem coś tam nabroić.

W sumie nawet sam niewiem poco robie tego singletona.
Może by było lepiej gdybym po prostu dał nagłowek ze statycznymi wskaźniki na obiekty i je zainicjował.

Pozdrawiam. Dziękuje za odpowiedzi.

0

A to wszystko dzieje się w jednym wątku czy kilku ? Jeśli w kilku to np. "new Object()" może iść równo z "getObjectById(...)", co w efekcie powoduje, że "Singleton::objects" przed wejściem w "getObjectById" jest jeszcze NULL'em, a po wejściu już jest inicjowane. Może brakuje ci synchronizacji.

Problem leży właśnie tam, gdzie tego nie pokazałeś. Postaw brake-pointa na problematycznym wywołaniu getObjectById, ale już po odczytaniu referencji np.

Objects* obj = ...; 
[BRAKE] obj->getObjectById(...);

to zobaczysz, że wskaźnik obj jest null'em. Jeśli nie jest to znaczy, że kompilator coś miesza. W specyficznych przypadkach w trybie debug mogą się pojawić różne kwiatki. W release jest ok ?

Generalnie porządne prześledzenie działania debugerem powinno pomóc.

Aha i jeszcze to o czym wspomniał quetzalcoatl, ukryj konstruktor i użyj coś takiego:

class Objects
{
private:
   Objects() {...}
public:
   CreateInstance() { if(Singleton::objects == NULL) Singleton::objects = new Objects();  }
}
0

Ustawiłbym jeszcze breakpointa na destruktorze:

Objects::~Objects()
{
        Singleton::objects = NULL;
}

bo być może Object jest gdzieś zadeklarowane lokalnie.

adf88 napisał(a)

ukryj konstruktor i użyj coś takiego:

Można jeszcze tak, bez bawienia się w późniejsze usuwanie obiektu:

class Objects
{
private:
	Objects(){ }
public:
	~Objects(){ }

	static Objects* Get();
};

Objects* Objects::Get()
{
	static Objects inst;
	return &inst;
}
0
0x666 napisał(a)

Można jeszcze tak, bez bawienia się w późniejsze usuwanie obiektu:

class Objects
{
private:
	Objects(){ }
public:
	~Objects(){ }

	static Objects* Get();
};

Objects* Objects::Get()
{
	static Objects inst;
	return &inst;
}

Wydawało mi się, że w momencie inicjalizacji Objects potrzebował jakieś przygotowane w runtime dane (ADDRESS), dopiero teraz się przyjrzałem że to zwykłe makro, więc jak najbardziej statyczny obiekt pasuje.

W takim przypadku jak podałeś to destruktor też wypada zrobić prywatnym.
I jeszcze prywatny konstruktor kopiujący.

1 użytkowników online, w tym zalogowanych: 0, gości: 1