#c #static #initialization #stateless
#c #статический #инициализация #без состояния
Вопрос:
Предположим, у меня есть класс T, где
- В T нет виртуальных функций.
- Экземпляры T не имеют состояния.
- T имеет статические экземпляры самого себя.
- Сам T не имеет другого состояния.
Может ли фиаско статической инициализации C разрушить мою программу? Я так не думаю, потому что даже если один из статических экземпляров не инициализирован перед использованием, это не должно иметь значения, потому что T объекты без состояния.
Я заинтересован в том, чтобы сделать это для классов, подобных перечислению, вот так:
// Switch.h
class Switch {
public:
static Switch const ON;
static Switch const OFF;
bool operator== (Switch const amp;s) const;
bool operator!= (Switch const amp;s) const;
private:
Switch () {}
Switch (Switch const amp;); // no implementation
Switch amp; operator= (Switch const amp;); // no implementation
};
// Switch.cpp
Switch const Switch::ON;
Switch const Switch::OFF;
bool Switch::operator== (Switch const amp;s) const {
return this == amp;s;
}
bool Switch::operator!= (Switch const amp;s) const {
return this != amp;s;
}
Комментарии:
1. Отличный вопрос и интересная идея!
2. что это дает вам по сравнению с простым перечислением (без дополнительного состояния)?
3. Пожалуйста, опубликуйте пример того, как вы на самом деле собираетесь использовать класс Switch.
4. Объявление класса находится в заголовке, а определения оператора и члена находятся в каком-то cpp-файле, верно? В противном случае, этот вопрос касается правила с одним определением, а не фиаско с порядком статической инициализации. Пожалуйста, укажите, является ли ваш пример кода двумя отдельными файлами.
5. Упс, извините. Это действительно должно быть в двух отдельных файлах.
Ответ №1:
Чтобы ответить на первую часть вашего вопроса, если T
есть конструктор, который имеет побочные эффекты, то вы действительно можете обжечься из-за фиаско статической инициализации.
Ответ №2:
Меня интересует, какие преимущества вы видите, скажем, от перечисления, заключенного либо в пространство имен, либо в класс:
namespace Switch {
enum Switch {
ON,
OFF
};
}
В большинстве случаев это будет проще в использовании (в вашей реализации вы требуете, чтобы пользователи использовали либо ссылки, либо указатели, поскольку объекты не копируются), это требует меньше кода (нет необходимости отключать конструкторы и создавать операторы)…
На самом деле, в готовящемся стандарте вы получите это практически бесплатно, даже без использования пространства имен:
enum Switch {
ON,
OFF
};
// bad, it allows this (as in the current standard):
Switch s = ON;
// good, it does also allow explicit qualification:
Switch s = Switch::ON;
Комментарии:
1. Я никогда не рассматривал это. Хотя, чтобы быть адвокатом дьявола, вариант класса, который я предложил, не допускает неявного преобразования в целые числа. Также вы не можете повторно открыть класс, но это можно решить, обернув класс вместо пространства имен, я думаю.
Ответ №3:
Вы действительно собираетесь использовать значения указателя для сравнения «состояния»? Я согласен с @Drew, это интересная идея. Я не уверен, что стандарт гарантирует ее работоспособность, хотя, если мы предположим, что это реализация только для заголовка.
Рассмотрим, что происходит, когда несколько объектов компиляции содержат одно и то же определение для Switch::ON
и Switch::OFF
. Поскольку это переменные, а не функции, компоновщику пришлось бы произвольно выбирать между ними.
Когда вы запускали тест, что сказали популярные компиляторы: gcc 3, gcc 4, Microsoft C 2005, 2008 и 2010 и один из компиляторов Edison Design Groups, такой какhttp://www.comeaucomputing.com / ?
Указанный тест будет состоять из:
// Switch.h
class Switch {
public:
static Switch const ON;
static Switch const OFF;
bool operator== (Switch const amp;s) const;
bool operator!= (Switch const amp;s) const;
private:
Switch () {}
Switch (Switch const amp;); // no implementation
Switch amp; operator= (Switch const amp;); // no implementation
};
Switch const Switch::ON;
Switch const Switch::OFF;
bool Switch::operator== (Switch const amp;s) const {
return this == amp;s;
}
bool Switch::operator!= (Switch const amp;s) const {
return this != amp;s;
}
и
// main.cpp
#include "Switch.h"
extern int another_test();
int main(int argc, char*argv[])
{
another_test();
const Switchamp; current_state = Switch::ON;
const Switchamp; another_state = Switch::OFF;
if (current_state == another_state) {
return 1;
} else if (current_state != another_state) {
return 2;
}
return another_test();
}
и
// another_test.cpp
#include "Switch.h"
int another_test()
{
const Switchamp; current_state = Switch::ON;
const Switchamp; another_state = Switch::OFF;
if (current_state == another_state) {
return 4;
} else if (current_state != another_state) {
return 5;
}
return 6;
}