C статическая инициализация класса без состояния

#c #static #initialization #stateless

#c #статический #инициализация #без состояния

Вопрос:

Предположим, у меня есть класс T, где

  1. В T нет виртуальных функций.
  2. Экземпляры T не имеют состояния.
  3. T имеет статические экземпляры самого себя.
  4. Сам 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;
}