Запрос об объявлении переменных класса в C++

У меня есть класс для представления трехмерного вектора поплавков:

class Vector3D
{
    public:

    float x, y, z;
    float * const data;

    Vector3D() : x(0.0), y(0.0), z(0.0), data(&x) {}
}

У меня вопрос: будут ли x, y и z размещаться последовательно в памяти, чтобы я мог присвоить адрес x данным, а затем использовать оператор индекса для данных для доступа к векторным компонентам как к массиву?

Например, иногда мне может понадобиться получить прямой доступ к векторным компонентам:

Vector3D vec;
vec.x = 42.0;
vec.y = 42.0;
vec.z = 42.0;

И иногда я могу захотеть получить к ним доступ по смещению:

Vector3D vec;
for (int i = 3; i--; )
    vec.data[i] = 42.0;

Будет ли второй пример иметь тот же эффект, что и первый, или я рискую перезаписать память, отличную от чисел с плавающей запятой x, y и z?


person milesleft    schedule 07.06.2011    source источник
comment
Интересная идея. Я не думаю, что это было бы хорошей идеей, но очень интересный вопрос.   -  person JHSaunders    schedule 08.06.2011
comment
Вместо явного получения адреса x вы пробовали offsetof(Vector3D, x) + this? Вам нужно будет убедиться, что все члены класса правильно выровнены.   -  person James    schedule 08.06.2011
comment
Просто любопытно, а зачем вообще данные? Вы можете просто реализовать оператор [] в Vector3D и/или привести указатель Vector3D * this к типу с плавающей запятой *. Вы достигаете того же эффекта, но с меньшим использованием памяти и синтаксисом.   -  person MerickOWA    schedule 08.06.2011
comment
Или реализовать объединение массива double[3] со структурой x,y,z   -  person MerickOWA    schedule 08.06.2011
comment
@MerickOWA: Вы хорошо заметили экономию памяти (у меня будет 1000 таких объектов). Я рассмотрю перегрузку operator[] .   -  person milesleft    schedule 08.06.2011
comment
@MerickOWA: приведение Vector3D* к float* вызывает UB, как и запись в один член союза и чтение из другого.   -  person ildjarn    schedule 08.06.2011
comment
@ildjarn это может быть не определено, но если мы предположим, что данные (& x) работают, то же самое сделают и другие альтернативы, которые я предложил.   -  person MerickOWA    schedule 08.06.2011
comment
@MerickOWA: data(&x) работает, пока data рассматривается как указатель на единственное число float, а не как указатель на массив float. Ни одна из предложенных вами альтернатив не является законной С++.   -  person ildjarn    schedule 08.06.2011
comment
@ildjarn: приведение Vector3D* к float* разрешено. 9.2p20 Указатель на объект структуры стандартного макета, соответствующим образом преобразованный с использованием reinterpret_cast, указывает на его начальный элемент (или, если этот элемент является битовым полем, то на модуль, в котором он находится) и наоборот.   -  person Ben Voigt    schedule 08.06.2011
comment
@BenVoigt: должным образом отмечено. Лично я еще не совсем готов применять формулировку C++0x к вопросам, не помеченным тегом C++0x, но я, по крайней мере, признаю, что это вероятно сработает на практике для любого разумно- недавний компилятор С++ 03 тоже.   -  person ildjarn    schedule 08.06.2011
comment
@ildjarn: Учитывая то внимание, которое комитет по стандартам уделяет существующим реализациям при рассмотрении новых правил, я бы сказал, что почти наверняка это так.   -  person Ben Voigt    schedule 08.06.2011


Ответы (6)


Нет, это поведение undefined по двум причинам:

  • Во-первых, для проблем с заполнением, о которых упоминали все остальные.
  • Во-вторых, даже если все заполнено правильно, недопустимо разыменовывать указатель со смещением, которое выводит его за пределы того, на что он указывает. Компилятор может предположить это и сделать оптимизации, которые приведут к неопределенному поведению, если вы его нарушите.

Тем не менее, следующее будет действительным:

class Vector3D
{
public:
    std::array<float,3> data;
    float &x, &y, &z;

    Vector3D() : data(), x(data[0]), y(data[1]), z(data[2]) { }
    Vector3D& operator =(Vector3D const& rhs) { data = rhs.data; return *this; }
};

std::array является новым для C++0x и в основном эквивалентен boost::array. Если вам не нужны C++0x или Boost, вы можете использовать std::vector (и изменить инициализатор на data(3)), хотя это гораздо более тяжелое решение, его размер может быть изменен из внешнего мира, и если это так, тогда результатом будет UB.

person Oliver Charlesworth    schedule 07.06.2011
comment
Это было бы правильно, но вынудило бы пользователя написать собственный оператор присваивания, поскольку он не будет определен автоматически. А добавление data() в список инициализации конструктора приведет к инициализации его содержимого нулями. - person ildjarn; 08.06.2011
comment
@ildjarn: Вы правы. Теперь я изменил свое предложение; vector не очень хорошо, но array должно быть лучше! - person Oliver Charlesworth; 08.06.2011
comment
@OliCharlesworth: я не предлагал вам отказаться от использования C-массива, а только добавить data в список инициализации и написать соответствующий оператор присваивания копирования. Тем не менее, я согласен, что std::array<>/boost::array<> здесь идеальны. - person ildjarn; 08.06.2011
comment
@ildjarn: я знаю. Но идея написать оператор присваивания копии для такого простого класса меня раздражает! выложу оба варианта... - person Oliver Charlesworth; 08.06.2011
comment
@OliCharlesworth: Кроме того, код, который у вас есть в настоящее время, очень сильно рискует вызвать UB, поскольку data является общедоступным - любой может изменить его размер или иным образом вызвать его перераспределение, после чего x, y и z обязательно будут недействительными. места памяти. - person ildjarn; 08.06.2011
comment
@ildjarn: Действительно. Надеюсь, все предостережения теперь учтены в моем ответе. Спасибо за ваши идеи! - person Oliver Charlesworth; 08.06.2011
comment
@OliCharlesworth: data также должен быть в списке инициализации для вашего первого образца - std::array<> - это тип POD, поэтому по умолчанию он останется неинициализированным. Кроме того, в обеих версиях по-прежнему требуются операторы копирования-присваивания, поскольку в обеих версиях элементы данных являются ссылками. Кроме того, они оба мне нравятся больше, чем версия vector. :-] - person ildjarn; 08.06.2011
comment
Извините, я хотел использовать данные типа float * const. Во всяком случае, мне нравится это решение. Есть ли какая-то конкретная причина, по которой вы бы использовали boost::array или std::vector вместо обычного массива? - person milesleft; 08.06.2011
comment
@milesleft: Если под обычным массивом вы подразумеваете C-массив, то обычным массивам нет места в современном коде C++. Это необходимая реликвия C, которую вы не должны использовать напрямую, если можете этого избежать. - person ildjarn; 08.06.2011
comment
@OliCharlesworth: C-массивы без проблем копируются неявными операторами копирования-присваивания; это ссылки, которые являются проблемой. ;-] - person ildjarn; 08.06.2011
comment
@ildjarn: Черт возьми, я устал! Вы действительно должны написать свой собственный ответ вместо меня, так как большая часть этого контента уже принадлежит вам! - person Oliver Charlesworth; 08.06.2011
comment
@OliCharlesworth: Ха-ха. Ваша первая версия была настолько близка к совершенству, что я думаю, вы заслуживаете репутации. Я просто отредактирую ваш ответ. :-П - person ildjarn; 08.06.2011
comment
@OliCharlesworth @ildjarn: Большое спасибо, ребята, это была поучительная дискуссия. Я действительно не думаю, что для меня большая проблема написать оператор присваивания копии :) - person milesleft; 08.06.2011
comment
Потенциальная проблема заключается в том, что класс будет намного больше, чем простой float[3], так как вы будете хранить 3 указателя в дополнение к данным. Это может иметь или не иметь значения в зависимости от того, для чего вы используете векторы. - person Michael Anderson; 08.06.2011
comment
@Майкл: Да, действительно. Хотя это всего на 2 указателя хуже, чем исходный код OP! - person Oliver Charlesworth; 08.06.2011
comment
Во-первых, как насчет именованных средств доступа, таких как x() и т. д., и вычисления доступа к массиву там? 2-й, если вы еще этого не сделали, пожалуйста, посмотрите этот вопрос и в начале предоставил ссылки на то, почему C-массивы все еще имеют свое место в современном C++ (хотя я пытаюсь разрушить этот последний оплот: P). - person Xeo; 08.06.2011
comment
@Xeo: Да, похоже, вы наткнулись на один допустимый вариант использования C-массивов, ха-ха. - person ildjarn; 08.06.2011
comment
@Oli: Не уверен, какова ваша вторая точка, но x, y и z все находятся в одном объекте, поэтому определены отношения указателей. - person Ben Voigt; 08.06.2011
comment
@BenVoigt: [нужна цитата] - person ildjarn; 08.06.2011
comment
@ildjarn: 5.9p2: если два указателя указывают на нестатические элементы данных одного и того же объекта или на подобъекты или элементы массива таких элементов, рекурсивно, указатель на более поздний объявленный элемент сравнивается больше при условии, что два члена имеют одинаковый доступ control (раздел 11) и при условии, что их класс не является объединением. - person Ben Voigt; 08.06.2011
comment
@BenVoigt: Сказать, что одно следует за другим, не то же самое, что сказать, что одно следует сразу за другим. - person ildjarn; 08.06.2011
comment
@Ildjarn: я знаю. Я собирался сказать, что арифметика указателя разрешена, но вовремя проверил стандарт, чтобы ослабить свой комментарий. Однако, поскольку типы одинаковы, отступов нет, один подобъект будет сразу же следовать за другим. - person Ben Voigt; 08.06.2011
comment
@BenVoigt: Мне придется подумать о вашем sizeof обосновании относительно заполнения между объектами одного типа завтра, когда я проснусь. На первый взгляд это звучит разумно, но я пока не готов к этому. :-П - person ildjarn; 08.06.2011
comment
@ildjarn: Это справедливо. Конечно, это просто примечание, в котором говорится, что заполнение вставляется только для целей выравнивания, что, строго говоря, не является обязательной частью стандарта. 5.3.3p2 может быть полезен: при применении к классу результатом является количество байтов в объекте этого класса, включая любое заполнение, необходимое для размещения объектов этого типа в массиве. По сути, я утверждаю, что у типа есть только одно требование выравнивания, которое в равной степени относится к элементам массива и членам структуры. - person Ben Voigt; 08.06.2011

да. Этот класс совместим с макетом стандартный макет, потому что:

  • У вас нет виртуальных функций.
  • Все элементы данных находятся в одном блоке спецификатора доступа (public:).

Из-за этого она гарантированно будет располагаться последовательно, как структура C. Это то, что позволяет вам читать и записывать заголовки файлов как структуры.

person Ben Voigt    schedule 07.06.2011
comment
Разве прокладка не является потенциальной проблемой? То есть, я не понимаю, какое отношение имеет компоновочная совместимость к тому, хранятся ли x, y и z в памяти последовательно. - person ildjarn; 08.06.2011
comment
Все, что вы сказали, звучит правильно. Но механизм ОП по-прежнему UB. - person Oliver Charlesworth; 08.06.2011
comment
Также, посмотрев стандарт, я не уверен, что компоновка-совместимость здесь актуальна. - person Oliver Charlesworth; 08.06.2011
comment
Компиляторам разрешено вставлять заполнение между двумя элементами данных, но им не разрешается изменять порядок. Непереносимое решение будет использовать директиву упаковки, в противном случае я бы выбрал @Решение Оли. - person jweyrich; 08.06.2011
comment
Вопрос в том, совместим ли макет struct { float x[3]; float * p;} с struct { float x,y,z; float * p;}. И я думаю, что ничего в том стандарте не говорит о том, что они есть (хотя часто компилятор будет выкладывать их одинаково). - person Michael Anderson; 08.06.2011
comment
@Michael: Каждый компилятор x64, который я использовал, по умолчанию не размещает их одинаково. Тем не менее, согласен, что ничего в стандарте не говорит о том, что они совместимы с макетом. - person ildjarn; 08.06.2011
comment
Как ни странно, g++ 4.0.1 в OS X, кажется, выкладывает их одинаково при использовании -arch x86_64 и с -arch i386 (хотя, конечно, пара 64b отличается от пары 32b). - person Michael Anderson; 08.06.2011
comment
@jweyrich: я никогда не слышал, чтобы между двумя членами данных одного типа вставлялись отступы, поскольку требования выравнивания выполняются автоматически. На самом деле sizeof (T) определяется как смещение между элементами T[]. - person Ben Voigt; 08.06.2011
comment
@ildjarn, @Oli: Мои извинения за использование неправильной терминологии. Теперь исправлено. - person Ben Voigt; 08.06.2011
comment
@BenVoigt: Разве «стандартный макет» не является новой концепцией для C ++ 0x? То есть, ваш ответ не специфичен для С++ 0x? В любом случае, мне не ясно, как это вообще относится к вопросу. Как в стандарте C++03, так и в FDIS C++0x конкретно указано, что Требования к выравниванию реализации могут привести к тому, что два соседних элемента не будут выделены сразу после друг друга -- разве это не позволяет законной реализации добавить заполнение между x, y и z в коде OP? - person ildjarn; 08.06.2011
comment
@ildjarn: я хотел бы увидеть пример ЛЮБОГО компилятора, который не размещает их одинаково. По определению требование выравнивания массива такое же, как выравнивание типа его элемента. - person Ben Voigt; 08.06.2011
comment
@ildjarn: у вас может быть отступ только между подобъектами разных типов. Способ определения sizeof с точки зрения расстояния между элементами массива гарантирует, что sizeof кратен требованию выравнивания. Следовательно, требования выравнивания реализации не могут требовать заполнения. А standard-layout, хотя он и может быть стандартизирован в C++0x, является просто признанием требования де-факто. - person Ben Voigt; 08.06.2011
comment
Если вы считаете, что ваш ответ относится к ожидаемому поведению на практике, это нормально, но я спрашиваю о том, что разрешено стандартом, и я не вижу здесь убедительных доказательств того, что код OP является законным в С++ 03. - person ildjarn; 08.06.2011
comment
@ildjarn: Хотите процитировать определение POD из C++03? У меня нет под рукой этой версии. Я думаю, что наличие нетривиального конструктора с нулевым аргументом дисквалифицирует этот тип, но без определения я не могу быть уверен. - person Ben Voigt; 08.06.2011
comment
На самом деле, это уже обсуждалось. - person Ben Voigt; 08.06.2011
comment
@BenVoigt : §3.9/10: Арифметические типы, типы перечисления, типы указателей и указатели на типы-члены, а также версии этих типов с указанием cv вместе называются скалярными типами. Скалярные типы, типы структур POD, типы объединения POD, массивы таких типов и версии этих типов с указанием cv вместе называются типами POD. (продолжение) - person ildjarn; 08.06.2011
comment
@BenVoigt: (продолжение) §9/4: Структура POD — это агрегатный класс, который не имеет нестатических элементов данных типа не-структура-POD, не-союз-POD (или массив таких типы) или ссылки и не имеет определяемого пользователем оператора присваивания копирования и определяемого пользователем деструктора. Точно так же POD-объединение является агрегатным объединением, которое не имеет нестатических членов данных типа не-POD-struct, не-POD-union (или массива таких типов) или ссылки, а также не имеет определяемого пользователем оператора присваивания копирования. и нет определяемого пользователем деструктора. Класс POD — это класс, который является либо структурой POD, либо объединением POD. - person ildjarn; 08.06.2011
comment
@ildjarn: Тогда структура в вопросе - C++ 03 POD? Я думал, что есть некоторые ограничения и на конструкторы... Я полагаю, тривиальное требование конструктора по умолчанию появилось в C++0x? О, подождите, я думаю, что требование тривиальных конструкторов является частью агрегированного класса. - person Ben Voigt; 08.06.2011
comment
@BenVoigt: Извините, пропустил. §8.5.1/1: Агрегат — это массив или класс без объявленных пользователем конструкторов, частных или защищенных нестатических элементов данных, базовых классов и виртуальных функций. Итак, вы Вы правы, думая, что любой тип с определяемым пользователем конструктором не может быть типом POD в C++03. - person ildjarn; 08.06.2011
comment
@ildjarn: Спасибо. Да, в С++ 03 любой определяемый пользователем конструктор освобождал тип от требований к макету POD, даже если у конструктора нет причин влиять на макет. Я думаю, именно поэтому POD был разделен на две классификации в C++0x: тривиальный и стандартный макет. - person Ben Voigt; 08.06.2011
comment
@Ben: Даже если мы можем предположить, что компилятор разложит их одинаково, я полагаю, что вы все равно получите UB из-за соображений, подобных псевдониму. - person Oliver Charlesworth; 08.06.2011
comment
@Oli: элементы массива и члены структуры имеют один и тот же тип, поэтому строгое присвоение псевдонимов не является проблемой ... для типов со стандартным макетом доступ к исходным членам с совместимым макетом допустим; родительские типы не обязательно должны быть одинаковыми, см. 9.2p19. К сожалению, стандарт не вышел прямо и не сказал, что массив является типом стандартной компоновки. - person Ben Voigt; 08.06.2011
comment
@Ben: я имею в виду псевдонимы в более общем смысле. Проблема в том, что стандарт не позволяет вам разыменовывать за пределами конца массива (и в этом отношении скаляр считается массивом длины один); поэтому компилятор может оптимизировать на основе этого предположения. Таким образом, такой код, как blah.y = 1; blah.data[1] = 2; std::cout << blah.y, может выводить 1 в зависимости от того, как компилятор организует доступ. - person Oliver Charlesworth; 08.06.2011
comment
@Ben: я считаю, что совместимость макетов применяется к структурам, а не к полям внутри структуры. - person Oliver Charlesworth; 08.06.2011
comment
@Oli: Зависят ли правила псевдонимов от правильно полученных указателей? Поскольку blah.data[1] — это *(blah.data + 1), а blah.data + 1 имеет тип float* и содержит адрес blah.y, использование такого указателя для доступа к переменной не вызывает проблем с алиасингом. - person Ben Voigt; 08.06.2011
comment
@Ben: по определению это псевдоним (en.wikipedia.org/wiki/Aliasing_( вычисления)). Вопрос в том, должен ли компилятор учитывать это при создании оптимизированного кода? Насколько я понимаю, это не так, потому что вы не должны разыменовывать за пределами границ. - person Oliver Charlesworth; 08.06.2011
comment
@Oli: Да, это псевдоним, но является ли он незаконным псевдонимом? Я так не думаю. blah.data + 1 является указателем на blah.y (при условии, что стандартное примечание гласит, что заполнение предназначено только для целей выравнивания). Требует ли алиасинг корректного получения указателя или он рассматривает только тип указателей (а в C99 — restrict)? - person Ben Voigt; 08.06.2011
comment
@Ben: В качестве отправной точки стандарт прямо говорит, что оценка чего-либо, кроме blah.data+0 и blah.data+1, не определена, поэтому это исключает, например, доступ к blah.z с помощью этого механизма. Что касается того, допустимо ли разыменовывать blah.data+1, стандарт C99 явно запрещает это. Я все еще ищу эквивалентную формулировку в стандарте C++. См. также: gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt08ch19s02.html . - person Oliver Charlesworth; 08.06.2011
comment
@Оли: раздел 5.7. Но это влияет только на то, является ли результирующий указатель безопасно полученным. Реализация определяет, имеет ли реализация ослабленную или строгую безопасность указателя. - person Ben Voigt; 09.06.2011
comment
Но это 3.10p11, который определил, когда компилятор не может предполагать псевдонимы, и они полностью основаны на типах. - person Ben Voigt; 09.06.2011
comment
@Ben: 5.7 прямо говорит, что, например, blah.data+2 не определено. В 3.10/11 нет формулировки, которая отменяет/отменяет это; он просто говорит, что все, что не включено в этот список, не определено. Материал о безопасных указателях — это новая идея в С++ 0x, и она не очень актуальна; он был введен, чтобы помочь определить доступность динамической памяти для реализаций, включающих сборку мусора. - person Oliver Charlesworth; 09.06.2011
comment
@Oli: Я думаю, вам, возможно, придется сначала привести структуру, совместимую с макетом, как это было предложено Майклом в одном из ранних комментариев, чтобы определить арифметику указателя. Но сама возможность сделать это эффективно требует реализации, чтобы blah.data + 2 работало. - person Ben Voigt; 09.06.2011

или вы можете иметь перегрузку operator[]

float operator[](int idx)
{
 switch (idx)
{
case 0:
  return x;
case 1:
  return y;
case 2:
 return z;
}
assert (false);
}
person pm100    schedule 08.06.2011
comment
Это должно быть float &. И вам также нужно будет предоставить версию const... - person Oliver Charlesworth; 08.06.2011
comment
Спасибо, это разумный вариант. - person milesleft; 08.06.2011

Компилятор обладает некоторой гибкостью в том, как он распределяет память внутри структуры. Структура никогда не будет перекрывать другую структуру данных, но она может вводить неиспользуемое пространство между элементами. В приведенной вами структуре некоторые компиляторы могут добавить 4 байта дополнительного пространства между z и данными, чтобы можно было выровнять указатель данных. Большинство компиляторов обеспечивают способ упаковки .

EDIT: нет гарантии, что компилятор решит упаковать x, y и z плотно, но на практике они будут хорошо упакованы, потому что они являются первыми элементами структуры и потому что они степень двойки по размеру.

person Mr Fooz    schedule 08.06.2011
comment
Проблема не в промежутке между z и data, а в промежутках между x, y и z. Есть и другие проблемы. - person Oliver Charlesworth; 08.06.2011

Ваше решение недействительно, но если вы можете гарантировать (или знать), что ваш компилятор "поступит правильно" (в частности, контролируя отступы между элементами x, y и z), все будет в порядке. В этом случае я бы вообще удалил член data и использовал operator[].

Я видел что-то подобное, используемое время от времени. Он сталкивается с точно такими же проблемами, но избавляет вас от хранения этого указателя данных и позволяет использовать более приятный синтаксис v[0], а не v.data[0].

class Vector3D
{
    public:

    float x, y, z;
    float& operator[](int i) { return *(&x+i); }
    const float& operator[](int i) const { return *(&x+i); }

    Vector3D() : x(0.0), y(0.0), z(0.0) {}
}

РЕДАКТИРОВАТЬ: По запросу ildjam здесь совместимая версия, использующая средства доступа, а не члены, что похоже.

class Vector3D
{
    public:
      float& operator[](int i) { return v[i]; }
      const float& operator[](int i) const { return v[i]; }

      float& x() { return v[0]; }
      float  x() const { return v[0]; }
      float& y() { return v[1]; }
      float  y() const { return v[1]; }
      float& z() { return v[2]; }
      float  z() const { return v[2]; }

      Vector3D() : v() {}
    private:    
      float v[3];
};
person Michael Anderson    schedule 08.06.2011
comment
Это страдает от того же UB, что и код OP - нет гарантии, что x, y и z будут храниться в памяти непрерывно. - person ildjarn; 08.06.2011
comment
Действительно, отсюда и возникают точно такие же проблемы. - person Michael Anderson; 08.06.2011
comment
Вы опубликовали ответ, чтобы продемонстрировать более удобный синтаксис для вызова UB? То есть зачем публиковать это вместо действительной, соответствующей реализации Vector3D, которая демонстрирует, как написать operator[]? - person ildjarn; 08.06.2011
comment
Я публикую то, что я видел в производственном коде, где были учтены (и задокументированы!) проблемы с производительностью, размером, удобочитаемостью и соответствием. Если вы знаете, что ваш компилятор делает с отступами, или можете управлять им с помощью прагм и т. д., то это решение может подойти. Для этого кода доступ к Vector3D через v[0] и v.x создает один и тот же машинный код, и каждый Vector3D не несет в себе лишнего мусора, который увеличивает его размер. Оба они имеют решающее значение, если вы используете Vector3D внутри основных циклов. - person Michael Anderson; 08.06.2011
comment
Но вы правы, это не ответ на явный вопрос. немного обновлю. - person Michael Anderson; 08.06.2011
comment
Такой код является причиной того, что многие компании испытывают такой кошмар, пытаясь выпустить 64-битные версии своих приложений. ;-] Было бы тривиально изменить этот код так, чтобы он соответствовал требованиям, производительность и размер оставались идентичными, а читабельность не сильно ухудшалась - так почему бы не опубликовать это? - person ildjarn; 08.06.2011
comment
Выложил похожую версию. У меня есть ощущение, что была веская причина (основанная на производительности) пойти по другому пути - и я думаю, что это могли быть старые компиляторы (я думаю, для встраиваемых систем), которые плохо справляются с конструктор по умолчанию, который убил некоторые основные циклы, но, вероятно, больше не действует, особенно для компиляторов младше 5 лет. - person Michael Anderson; 08.06.2011
comment
Также обратите внимание, что Vector3D() : v() { } является сокращением от Vector3D() : { v[0]=0; v[1]=0; v[2]=0; } и, возможно, более эффективным. - person ildjarn; 08.06.2011
comment
Отмечено и обновлено - на самом деле конструктор по умолчанию должен делать правильные вещи - но обычно Vector3 будет иметь конструкторы не по умолчанию, поэтому его более ясно, чтобы быть явным. - person Michael Anderson; 08.06.2011
comment
@Michael: Нет, неявный конструктор по умолчанию определенно не будет работать правильно - float и float[3] являются типами POD, поэтому автоматическая инициализация не выполняется (тогда как v() вызывает инициализацию значения). - person ildjarn; 08.06.2011

Сделайте что-то вроде этого:

float data[3];
float& x, y, z;

    Vector3D() : x(data[0]), y (data[1]), z(data[2]) { data [0] = data [1] = data [2] = 0;}
person Rajivji    schedule 07.06.2011