#c #pointers #casting
#c #указатели #Кастинг
Вопрос:
Допустим, у меня есть два разных класса, оба представляют данные 2D-координат одним и тем же внутренним способом, например, следующим:
class LibA_Vertex{
public:
// ... constructors and various methods, operator overloads
float x, y
};
class LibB_Vertex{
public:
// ... same usage and internal data as LibA, but with different methods
float x, y
};
void foobar(){
LibA_Vertex * verticesA = new LibA_Vertex[1000];
verticesA[50].y = 9;
LibB_Vertex * verticesB = reinterpret_cast<LibB_Vertex*>( vertexA );
print(verticesB[50].y); // should output a "9"
};
Учитывая, что два класса идентичны и функция выше, могу ли я надежно рассчитывать на то, что это преобразование указателя будет работать должным образом в каждом конкретном случае?
(Предыстория в том, что мне нужен простой способ обмена массивами вершин между двумя отдельными библиотеками, которые имеют идентичные классы вершин, и я хочу избежать ненужного копирования массивов).
Ответ №1:
В C 11 добавлена концепция, называемая layout-compatible, которая применяется здесь.
Два типа структуры стандартной компоновки (пункт 9) совместимы с компоновкой, если они имеют одинаковое количество нестатических элементов данных, а соответствующие нестатические элементы данных (в порядке объявления) имеют типы, совместимые с компоновкой (3.9).
где
Класс стандартной компоновки — это класс, который:
- не имеет нестатических элементов данных типа нестандартного класса компоновки (или массива таких типов) или ссылки,
- не имеет виртуальных функций (10.3) и виртуальных базовых классов (10.1),
- имеет одинаковый контроль доступа (пункт 11) для всех нестатических элементов данных,
- не имеет базовых классов нестандартной компоновки,
- либо не имеет нестатических элементов данных в самом производном классе и не более одного базового класса с нестатическими элементами данных, либо не имеет базовых классов с нестатическими элементами данных, и
- не имеет базовых классов того же типа, что и первый нестатический элемент данных.
Структура стандартной компоновки — это класс стандартной компоновки, определенный с помощью class-key
struct
или class-keyclass
.Объединение стандартной компоновки — это класс стандартной компоновки, определенный с помощью class-key
union
.
Наконец
Указатели на cv-qualified и cv-unqualified версии (3.9.3) типов, совместимых с макетом, должны иметь одинаковые требования к представлению значений и выравниванию (3.11).
Что гарантирует, что reinterpret_cast
может превратить указатель на один тип в указатель на любой тип, совместимый с макетом.
Комментарии:
1. Другим четко определенным способом выполнения этих преобразований является использование объединения с общей начальной последовательностью (как разрешено
§9.2/19
).2. О! Что ж, C 11 на помощь. Я просто надеюсь, что это было среди того, что VS2010 решил добавить, когда он выбрал вишню через стандарт.
3. @Clairvoire: Это одна из вещей, которая всегда работала на практике, хотя и была запрещена формально. Я не ожидаю, что какие-либо авторы компилятора должны были «добавлять» поддержку.
4. Это никогда не было «запрещено», это было просто «неопределенное поведение». Просто фактическое поведение в каждом компиляторе, о котором я знаю, должно было работать так, как вы ожидаете. C 11 просто в основном определял поведение, которое в любом случае использовал каждый компилятор.
Ответ №2:
Я бы обернул это преобразование в класс (чтобы, если вам нужно сменить платформу или что-то в этом роде, оно было хотя бы локализовано в одном месте), но да, это должно быть возможно.
Вы захотите использовать reinterpret_cast
, не static_cast
так хорошо.
Комментарии:
1. Вы правы, моя ошибка! Исправлен вопрос
Ответ №3:
Теоретически это неопределенное поведение. Однако это может работать в определенных системах / платформах.
Я бы посоветовал вам попытаться объединить 2 класса в 1. т.е.
class Lib_Vertex{
// data (which is exactly same for both classes)
public:
// methods for LibA_Vertex
// methods for LibB_Vertex
};
Добавление методов в a class
не повлияет на его размер. Возможно, вам придется немного изменить свой дизайн, но оно того стоит.
Комментарии:
1. Обычно я бы тоже использовал что-то подобное. Мне все равно пришлось бы делать копии массивов. Одна библиотека (2d physics lib) возвращает целые массивы вершин, используя свой внутренний класс вершин, который затем мне нужно передать в другую библиотеку (2d rendering lib), которая принимает массивы своего внутреннего класса вершин.
Ответ №4:
Технически это неопределенное поведение. В действительности, если для компиляции обоих классов использовался один и тот же компилятор, они будут иметь одинаковое расположение в памяти, если поля объявлены в одинаковом порядке, имеют одинаковые типы и одинаковый уровень доступа.