Является ли этот код причиной неопределенного поведения? Или я могу столкнуться с проблемой с этим? (скопируйте полный класс без функций, просто переменные с публичным модификатором и измените личные memebers, чтобы этот указатель):
#include <iostream>
using namespace std;
class Point {
private:
int x;
int y;
public:
Point(int x, int y) {
this->x = x;
this->y = y;
}
void Print() {
cout << "(" << x << ", " << y << ")" << endl;
}
};
struct PointHack {
int x;
int y;
};
int main() {
Point a(4, 5);
a.Print();
((PointHack *) (&a))->x = 1;
((PointHack *) (&a))->y = 2;
a.Print();
return 0;
}
вывод:
(4, 5)
(1, 2)
(конечно, с исходным заказом участника)
Несмотря на то, что ваши классы совместимы с макетами (см. Ниже), ваш код демонстрирует неопределенное поведение из-за того, что такие приведения указателей запрещены правилами C++ строгого сглаживания 1.
Но: заменяя броски на union
делает код стандартным; на самом деле это гарантировано в работе C++ 11:
#include <iostream>
using namespace std;
class Point {
private:
int x;
int y;
public:
Point(int x, int y) {
this->x = x;
this->y = y;
}
void Print() {
cout << "(" << x << ", " << y << ")" << endl;
}
};
struct PointHack {
int x;
int y;
};
union pu
{
Point p;
PointHack ph;
pu(int x, int y) : p(x, y) {}
};
int main() {
pu u(4,5);
u.p.Print();
u.ph.x=1;
u.ph.y=2;
u.p.Print();
return 0;
}
Это происходит из-за того, что Point
и PointHack
являются PointHack
стандартного макета 2 (C++ 11, §9 ¶7) и разделяют "общую начальную подпоследовательность" (§9.2, ¶20); как таковые, если они оба хранятся в одном объединении (здесь pu
), ему разрешено "проверять общую начальную часть любого из них" 3.
Тем не менее, этот ответ в основном представляет собой упражнение стиля; не используйте такие трюки, если вы действительно не вынуждены. C++ предоставляет более эффективные средства доступа к частным членам, если это необходимо, без жестокого нарушения инкапсуляции - у вас есть геттеры/сеттеры, защищенные наследования, классы друзей... И вообще, если вы получаете доступ к членам частного класса способами, не предназначенными для вашей цели класса, вы потенциально нарушаете предположения этого класса о том, как его данные изменяются, что может привести к неустойчивому поведению кода его методов.
Заметки:
ph
делает ее "активным объектом" union
, а затем p.Print()
является "проверкой" "неактивного" объекта.Да, выложенный код вызывает неопределенное поведение. Не делай этого.
Если вы хотите увидеть по-настоящему безумный способ, которым некоторые люди справляются с этой сомнительной целью, здесь вы идете: получить доступ к частному члену, используя шаблонный трюк
Но на самом деле, не делай этого.
PS: никогда не используйте C-style cast в C++. Используйте static_cast, dynamic_cast, reinterpret_cast и const_cast, если это необходимо. C-style casts бесполезно небезопасны.
я нашел проблему
/*code:*/
#include <stdio.h>
void check(short *h,long *k)
{
*h=5;
*k=6;
if (*h == 5)
printf("strict aliasing problem\n");
}
int main(void)
{
long k[1];
check((short *)k,k);
return 0;
}
/*
$ gcc -Wall -ansi -pedantic -O0 -o o0 asd.c
$ ./o0
/output: nothing/
$ gcc -Wall -ansi -pedantic -O2 -o o2 asd.c
$ ./o2
/output: strict aliasing problem/
*/
(так же, как в c++)