Простой способ найти неинициализированные переменные-члены
Я ищу простой способ обнаружить неинициализированные переменные-члены класса.
Нахождение таких переменных как во время выполнения, так и на этапе компиляции подходит.
На данный момент я ставлю точку останова в конструкторе класса и проверяю переменные по одной.
5 ответ(ов)
Если вы используете GCC, вы можете применить флаг -Weffc++
, который генерирует предупреждения, когда переменная не инициализирована в списке инициализации членов. Например:
class Foo
{
int v;
Foo() {}
};
Приведет к следующему предупреждению при компиляции:
$ g++ -c -Weffc++ foo.cpp -o foo.o
foo.cpp: In constructor ‘Foo::Foo()’:
foo.cpp:4: warning: ‘Foo::v’ should be initialized in the member initialization list
Однако есть и недостатки: флаг -Weffc++
также будет предупреждать вас, если переменная имеет корректный конструктор по умолчанию и инициализация не обязательна. Он также выдает предупреждения, когда вы инициализируете переменную в конструкторе, но не в списке инициализации членов. Дополнительно он дает предупреждения по ряду других стилей C++, таких как отсутствие конструкторов копирования, так что вам, возможно, придется немного почистить свой код, если вы хотите регулярно использовать -Weffc++
.
Существует также ошибка, которая вызывает постоянное предупреждение при использовании анонимных объединений, и единственный способ обойти это — отключить предупреждение с помощью:
#pragma GCC diagnostic ignored "-Weffc++"
Несмотря на это, я нашел -Weffc++
невероятно полезным для выявления множества распространенных ошибок в C++.
Флаг компилятора -Wuninitialized
используется для проверки использования неинициализированных переменных. Это означает, что компилятор будет предупреждать вас, если переменная может быть использована до того, как ей будет присвоено какое-либо значение.
В приведенном вами примере кода:
struct Q {
int x, y;
Q() : x(2) {}
int get_xy() const { return x * y; }
};
Компилятор g++
не выдаст предупреждение о неинициализированной переменной y
, если вы вызовете метод get_xy()
, поскольку y
не инициализирован, и вы собираетесь использовать его в выражении return x * y;
. Предупреждение появится только в том случае, если вы вызовете метод get_xy()
без присвоения значения переменной y
.
Таким образом, флаг -Wuninitialized
полезен для выявления потенциальных ошибок в коде, связанных с использованием переменных, значения которых не были определены. Однако, стоит отметить, что он может не обнаружить все возможные случаи, особенно в сложных конструкциях, где использование переменных может зависеть от логики программы.
Если вы используете Visual Studio, вы можете скомпилировать программу в режиме отладки, остановить ее в отладчике и проверить, какие переменные инициализированы байтами, содержащими 0xCC (стек) или 0xCD (куча).
Тем не менее, я бы на вашем месте рассматривал возможность использования инструмента статического анализа для более тщательного подхода.
В Visual Studio (MSVC) есть опция компилятора /sdl (включить дополнительные проверки безопасности) (http://msdn.microsoft.com/en-us/library/jj161081.aspx). Эта опция в режиме выполнения:
- Выполняет инициализацию членов класса. Автоматически инициализирует члены класса типа указателя в ноль при создании объекта (до выполнения конструктора). Это помогает предотвратить использование неинициализированных данных, связанных с членами класса, которые конструктор не инициализирует явно.
Следует отметить, что эта опция не поможет вам обнаружить неинициализированные переменные-члены на этапе компиляции, но делает поведение программы более предсказуемым, когда это происходит в режиме выполнения. Тем не менее, не стоит рассчитывать на то, что эта опция будет включена в коде, который вы пишете.
Будьте осторожны! Компиляторские опции, предложенные здесь, не являются ни надежными, ни независимыми от версии. Рассмотрим простой пример:
class A {
int a;
public:
void mA() {
printf("haha");
++a;
int g = 2/a;
printf("%i\n",g);
}
};
int main() {
A a;
a.mA();
}
При компиляции с g++ -O3 -Weffc++ -Wuninitialized
данное код вызывает предупреждение о "неинициализированной" переменной на версиях gcc до 4.6 включительно, тогда как на 4.7 и 4.8 (тестировалось на MacPorts) он проходит без проблем. Интересно, что если мы уберем printf("haha");
, то версии 4.7 и 4.8 вновь начинают видеть "неинициализированную" переменную A::a
. Clang
немного лучше в этом плане, так как он как-то присваивает мусор (вместо удобного 0
) неинициализированным переменным, что позволяет быстрее и проще заметить их катастрофические последствия.
Также мне не удалось обнаружить вышеупомянутую неинициализированную переменную A::a
с помощью valgrind
; может, уважаемые коллеги, предлагающие valgrind
, могут предоставить подходящие опции для обнаружения этой ошибки.
В итоге: отличный вопрос, но в данный момент надежных решений не так уж много... (как я это вижу).
Имеют ли круглые скобки после имени типа значение при использовании new?
Каковы преимущества инициализации списка (с использованием фигурных скобок)?
Как проще всего инициализировать std::vector с жестко заданными элементами?
Как инициализировать приватные статические члены данных в заголовочном файле
Как инициализировать статическую Map?