Dostęp do chronionego członka poprzez member-pointer: czy to hack?
Wszyscy wiemy, że elementy określone protected
z klasy bazowej mogą być dostępne tylko z własnej instancji klasy pochodnej. Jest to cecha ze standardu, która była wielokrotnie omawiana na Stack Overflow:
- nie można uzyskać dostępu do chronionego członka innej instancji z zakresu typu pochodnego ;
- Dlaczego mój obiekt nie może uzyskać dostępu do chronionych członków innego obiektu zdefiniowanego w wspólnej klasie bazowej? I inni
Ale wydaje się możliwe obejście tego ograniczenia za pomocą wskaźników członkowskich, ponieważ użytkownik chtz pokazał mi :
struct Base { protected: int value; };
struct Derived : Base
{
void f(Base const& other)
{
//int n = other.value; // error: 'int Base::value' is protected within this context
int n = other.*(&Derived::value); // ok??? why?
(void) n;
}
};
Dlaczego jest to możliwe, czy jest to pożądana cecha, czy usterka gdzieś w implementacji lub brzmieniu standardu?
Z komentarzy wyłoniło się kolejne pytanie: Jeśli Derived::f
jest wywołana z rzeczywistym Base
, czy to nieokreślone zachowanie?
4 answers
Fakt, że członek nie jest dostępny przy użyciu class member access expr.ref (aclass.amember
) ze względu na kontrolę dostępu [klasy.access] nie czyni tego elementu niedostępnym przy użyciu innych wyrażeń.
Wyrażenie &Derived::value
(którego typem jest int Base::*
) jest całkowicie zgodny ze standardem i oznacza element value
z Base
. Następnie wyrażenie a_base.*p
Gdzie p
jest wskaźnikiem do członka Base
i a_base
instancja Base
jest również zgodny ze standardem .
Tak więc każdy kompilator zgodny ze standardem tworzy wyrażenie other.*(&Derived::value);
zdefiniowane zachowanie: dostęp do elementu value
z other
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-29 10:42:24
Czy to włamanie?
W podobny sposób jak używanie reinterpret_cast
, może to być niebezpieczne i potencjalnie może być źródłem trudnych do znalezienia błędów. Ale jest dobrze uformowany i nie ma wątpliwości, czy powinien działać.
Dla wyjaśnienia analogii: zachowanie reinterpret_cast
jest również dokładnie określone w standardzie i może być używane bez żadnego UB. Ale reinterpret_cast
omija system typów, a system typów istnieje nie bez powodu. Podobnie, ten wskaźnik do członka trick jest dobrze uformowany zgodnie ze standardem, ale omija enkapsulację prętów, a to enkapsulacja (typowo) istnieje nie bez powodu (mówię typowo, bo przypuszczam, że programista może używać enkapsulacji frywolnie).
[czy to] usterka gdzieś w implementacji czy sformułowaniu standardu?
Nie, implementacja jest poprawna. W ten sposób został określony język.
Funkcja członka Derived
może oczywiście uzyskać dostęp &Derived::value
, ponieważ jest chronionego członka bazy.
Wynikiem tej operacji jest wskaźnik do członka Base
. Można to zastosować do odniesienia do Base
. Uprawnienia dostępu dla członków nie mają zastosowania do wskaźników dla członków: mają zastosowanie tylko do nazw członków.
Nie UB.Z komentarzy pojawiło się kolejne pytanie: jeśli Pochodna:: f jest wywoływana z rzeczywistą bazą, to czy jest to nieokreślone zachowanie?
Base
ma członka.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-29 10:24:41
Zasadniczo to, co robisz, to oszukiwanie kompilatora, i to powinno zadziałać. Zawsze widzę tego rodzaju pytania i ludzie czasami mają złe wyniki, a czasami działa, w zależności od tego, jak to konwertuje do kodu asemblera.
Pamiętam, że widziałem przypadek ze słowem kluczowym const
Na liczbie całkowitej, ale potem jakimś podstępem facet był w stanie zmienić wartość i skutecznie obejść świadomość kompilatora. Rezultatem było: Błędna wartość dla prostej matematycznej operacja. Powód jest prosty: Assembly w x86 robi rozróżnienie między stałymi i zmiennymi, ponieważ niektóre instrukcje zawierają stałe w swoim kodzie opcode. Tak więc, ponieważ kompilator uważa, że jest stałą, potraktuje ją jako stałą i poradzi sobie z nią w zoptymalizowany sposób z niewłaściwą instrukcją procesora, a baam, masz błąd w wynikowej liczbie.
Innymi słowy: kompilator będzie próbował wyegzekwować wszystkie reguły, które może wyegzekwować, ale prawdopodobnie możesz w końcu oszukać go, i może lub nie może uzyskać złe wyniki w oparciu o to, co próbujesz zrobić, więc lepiej zrobić takie rzeczy tylko wtedy, gdy wiesz, co robisz.
W Twoim przypadku wskaźnik &Derived::value
może być obliczony na podstawie liczby bajtów od początku klasy. Jest to w zasadzie sposób, w jaki kompilator uzyskuje do niego dostęp, więc kompilator:
- nie widzi żadnego problemu z uprawnieniami, ponieważ uzyskujesz dostęp do
value
poprzezderived
w czas kompilacji. - można to zrobić, ponieważ w obiekcie o tej samej strukturze co
derived
(NO, oczywiście,base
) bierzemy przesunięcie w bajtach.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-29 08:42:11
Żeby dodać do odpowiedzi i przybliżyć trochę horror, który mogę przeczytać między twoimi wierszami. Jeśli widzisz, że access specifiiers to "prawo", które chroni Cię przed robieniem "złych rzeczy", myślę, że o to ci chodzi. public
, protected
, private
, const
... są częścią systemu, który jest ogromnym plusem dla C++. Języki bez niego mogą mieć wiele zalet, ale gdy budujesz duże systemy, takie rzeczy są prawdziwym atutem.
Mówiąc, że: myślę, że to dobrze, że można uzyskać wokół prawie wszystkich siatek zabezpieczających dostarczonych do ciebie. O ile pamiętacie, że "możliwe" nie znaczy "dobre". Dlatego nigdy nie powinno to być "łatwe". Ale co do reszty-to zależy od Ciebie. Jesteś architektem.
Lata temu mogłem po prostu to zrobić (i nadal może działać w niektórych środowiskach):
#define private public
Bardzo pomocny dla' wrogich ' zewnętrznych plików nagłówkowych. Dobra praktyka? Co o tym myślisz? Ale czasami opcje są ograniczone.
Więc tak, to co pokazujesz jest rodzajem naruszenia w systemie. Ale hej, co powstrzymuje cię przed pozyskiwaniem i rozdawaniem publicznych odniesień do członka? Jeśli straszne problemy z konserwacją Cię włączają-za wszelką cenę, dlaczego nie?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-04 12:08:33