проблемы со статическими членами - PullRequest
1 голос
/ 10 августа 2009

Предполагая, что у меня есть этот класс в C ++:

class A
{
public:
    static const TDouble pi_d;
    static const TDouble pi;
    static const TDouble div;
};

Они инициализируются в файле .h следующим образом:

const TDouble A::pi_d = 3.14;
const TDouble A::pi   = A::pi_d;
const TDouble A::div  = A::pi / 180.0;

Когда я печатаю элемент div, результат равен 0,0000. Если я изменю эту строку:

const TDouble A::pi   = A::pi_d;

с этой строкой:

const TDouble A::pi   = 3.14;

тогда все в порядке и выводится правильное значение.

Вы знаете, в чем причина?

Спасибо, что уделили время. Асаф.

Ответы [ 4 ]

7 голосов
/ 10 августа 2009

Ответы, относящиеся к «порядку инициализации статики», верны для объектов, которые определены в разных единицах перевода, но в одном и том же TU определен порядок:

9.4.2 / 7:

Статические члены данных инициализируются и уничтожаются точно так же, как нелокальные объекты (3.6.2, 3.6.3).

3.6.2 / 1:

Объекты со статической продолжительностью хранения, определенной в области пространства имен в том же переводе единица и динамически инициализируется должны быть инициализированы в том порядке, в котором их определение появляется в переводческий блок.

Ваши определения упорядочены правильно, поэтому их следует инициализировать в правильном порядке. Я хотел бы пойти с комментарием AProgrammer относительно нескольких определений. В отличие от const объектов, не являющихся членами, ваши объекты имеют внешнюю связь:

3,5 / 5

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

Если заголовочный файл включен в несколько блоков перевода, то код нарушает ODR, и я полагаю, что результатом неопределенного поведения является странность с инициализацией. Попробуйте поместить определение статических членов в один исходный файл и посмотрите, что произойдет.

2 голосов
/ 10 августа 2009

Стандарт, 3.6.2, говорит, что такая инициализация должна работать, делая несколько предположений.

«Объекты типов POD (3.9) со статической продолжительностью хранения, инициализированной с помощью константных выражений (5.19), должны быть инициализированы перед любой динамической инициализацией». Поэтому присвоение 3.14 должно произойти до любой другой инициализации.

«Объекты со статической длительностью хранения, определенной в области пространства имен в одной и той же единице перевода и динамически инициализированной, должны быть инициализированы в том порядке, в котором их определение появляется в единице перевода». Поскольку pi появляется перед div в определении, его следует инициализировать первым.

Я предполагаю, что утверждения такие же, как вы их дали, и что TDouble - это какая-то причудливая версия double. Если TDouble - класс с реальным поведением, это усложняет ситуацию. Точно так же вы не показываете контекст кода или даже указывает, является ли файл .h #include d более чем в одном файле .c (в этом случае у вас возникнут проблемы с правилом единого определения). В качестве альтернативы, вы можете не использовать стандартный компилятор.

Итак, каково определение TDouble? Каков контекст для строк? Какой компилятор вы используете и с какими параметрами компилятора?

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

1 голос
/ 10 августа 2009

Невозможно ответить на этот вопрос, если вы не дадите нам больше информации о том, что именно является типом Double.

Мое предположение из примера состоит в том, что TDouble имеет либо ошибку преобразования с потерями, либо неправильно реализует оператор / (по существу, преобразование в int перед делением). Но это просто предположение, если мы не увидим тип.

0 голосов
/ 10 августа 2009

Порядок инициализации статических элементов не определен. Использование одного статического элемента для инициализации другого не будет работать надежно. Маловероятно, что вам действительно нужны два постоянных статических члена, поэтому я бы сказал, просто исключите один.

Конечно, вероятно, в вашем реальном коде происходит нечто более тонкое. Просто помните, что вы не можете рассчитывать на порядок инициализации констант больше, чем вы можете рассчитывать на порядок инициализации полей в списке инициализации конструктора, и напишите свой код соответствующим образом. Случайный макрос может помочь.

#define PI 3.14
const TDouble A::pi_d = PI;
const TDouble A::pi   = PI;
const TDouble A::div  = PI / 180.0;

Это работает, потому что макросы являются текстовой заменой и не могут быть переупорядочены так, как это может сделать статическая инициализация элементов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...