Rzutowanie wartości 1 na wskaźnik

0
  1. Co powinien robić zapis:
(int)(Typ*)1

Jak dla mnie to jest zrzutowanie wartości 1 na typ wskaźnikowy i zrzutowanie tego wskaźnika na int w celu osiągnięcia adresu, który i tak wychodzi 0x1.
Otóż pytam, bo natknąłem się w książce na klasę singleton

template<class T>
class Singleton
{
	static T* ms_Singleton;
public:
	Singleton()
	{
		assert( !ms_Singleton );
		int offset = (int)(T*)1 - (int)(Singleton<T>*)(T*)1;
		ms_Singleton = (T*)((int)this + offset);
	}

	~Singleton()
	{
		assert( ms_Singleton);
		ms_Singleton = 0;
	}

	static T& GetSingleton()
	{
		assert( ms_Singleton );
		return *ms_Singleton;
	}

	static T* GetSingletonPtr()
	{
		return ms_Singleton;
	}
};

Tak więc chodzi mi o wyliczenie offsetu. W moim programie offset zawsze wychodzi 0, bo rzutowanie takie jak pokazałem wyżej daje mi cały czas wartość 1.

  1. Nie będę zakładał drugiego tematu. Ponieważ czytam standard C++11 chciałbym się dowiedzieć, czy te konstruktory są sobie równoznaczne?
struct C
{
	std::string s;
	//...
	C( const C& x ) : s( x.s ) { }
	C( C&& x ) : s(static_cast< std::string&& >( x.s )) { }
};

Jeżeli nie, to jaka jest między nimi różnica?

0

" Cała ważna praca wykonywana jest w konstruktorze Singleton, gdzie obliczamy względny adres pochodnej klasy o zapisujemy wynik tej operacji we wskaźniku singletonu (ms_Singleton). Zauważ, że pochodna klasa może wywodzić się z więcej niż jednej klasy Singleton, ale w takim wypadku this klasy MyClass może być inne od this klasy Singleton. Rozwiązaniem jest pobranie nieistniejącego obiektum który znajduje się w pamięci pod adresem 0X1, rzutowanie go na obydwa typy, i sprawdzenie różnicy. Różnica będzie w rzeczywistości odległością między Singleton <MyClass> i jego pochodnym typem MyClass, którego potem można użyć do obliczenia wskaźnika singletonu."
Scott Bilas

źródło

czy aby nie to było napisane w tej książce? Nie jest to odpowiedź na twoje pytanie?

0

Co nie zmienia faktu, że dalej mi nie wytłumaczyłeś co oznacza zapis:

(int)(T*)1 - (int)(Singleton<T>*)(T*) /*czemu tutaj rzutujemy na oba typy?*/1

Równie dobrze, mogłem powiedzieć w jakiej książce to było.
Otóż stworzyłem sobie przykład:

#include <iostream>
using namespace std;

template<class T>
class S
{
public:
	static T* ms_c;
	static int offset;
	S()
	{
		offset = (int)(T*)1 - (int)(S<T>*)(T*)1;
		ms_c = (T*)((int)this + offset);
		cout << offset;
	}
	~S()
	{
		ms_c = 0;
	}

	static T& Get() { cout << offset; return *ms_c; }
};

template < class T > T* S < T >::ms_c = NULL;
template < class T > int S< T >::offset = 0;

class C : public S<C>
{
public:
	int Dupa;
	C() {}
	~C() {}
};

#define g_C C::Get()
 
int main()
{
	g_C;
	getchar();
	return 0;
}

I to się nawet nie kompiluje, a co dopiero o odczytaniu offsetu

kompilator napisał(a)

1>main.obj : error LNK2001: unresolved external symbol "public: static class C * S<class C>::ms_c" (?ms_c@?$S@VC@@@@2PAVC@@A)
1>main.obj : error LNK2001: unresolved external symbol "public: static int S<class C>::offset" (?offset@?$S@VC@@@@2HA)
1>C:\Users\MJay\Documents\Visual Studio 2010\Projects\Sprawdzacz\Debug\Sprawdzacz.exe : fatal error LNK1120: 2 unresolved externals

Nie wiem czemu linker pluje się o to, skoro najwidoczniej to powinno działać.

0

Wygląda mi to na jakiś bardzo brzydki hack opierający się tylko i wyłącznie na compiler internals.

0
MJay napisał(a)

I to się nawet nie kompiluje, a co dopiero o odczytaniu offsetu
a rozmiar wskaźnika mieści się w int u ciebie? U mnie pomogło na (debian 64bit) rzutowanie na long int, powyższa wersja również się nie kompilowała.

#include <iostream>
using namespace std;

template<class T>
class S
{
public:
        static T* ms_c;
        static int offset;
        S()
        {
                offset = (long int)(T*)1 - (long int)(S<T>*)(T*)1;
                ms_c = (T*)((long int)this + offset);
                cout << offset;
        }
        ~S()
        {
                ms_c = 0;
        }

        static T& Get() { cout << offset; return *ms_c; }
};


template < typename T > T * S < T >::ms_c = 0;
template < typename T >int S < T >::offset= 0;


class C : public S<C>
{
public:
        int Dupa;
        void print() { cout << "Dziala!"; }
        C() {}
        ~C() {}
};

#define g_C C::Get()

int main()
{
        g_C.print();
        cin.sync();
        cin.get();

        return 0;
}
0

Dzięki, twój kod rzeczywiście działa, ale z racji, że nie czaję wyliczania tego offsetu, rozwiązałem to inaczej:

#include <iostream>
using namespace std;
 
template<class T>
class S
{
public:
        static T* ms_c;
        S()
        {
                ms_c = new T;
        }
        ~S()
        {
                delete ms_c;
        }
 
        static T& Get() { return *ms_c; }
};

template < class T > T* S< T >::ms_c = NULL;
 
class C : public S<C>
{
public:
        int Dupa;
        C() {}
        ~C() {}
};
 
#define g_C C::Get()
 
int main()
{
        g_C;
        getchar();
        return 0;
}
1

offset będzie rożny od zera tylko, gdy ten cytat ma sens: "Zauważ, że pochodna klasa może wywodzić się z więcej niż jednej klasy Singleton". Czyli bardzo bardzo rzadko, musisz robić naprawdę dziwne rzeczy, by natrafić na coś takiego.
W sumie skoro używa C++ to nie rozumiem czemu nie zastosował static_cast<T*>(this), co zrobiłoby dokładnie to samo co ten porąbany offset.

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