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.

  2. 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="C">::ms_c" ([email protected][email protected]@@@@[email protected]@A)
1>main.obj : error LNK2001: unresolved external symbol "public: static int S<class c="C">::offset" ([email protected][email protected]@@@@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, botów: 0