C ++ дизайн со статическими методами - PullRequest
5 голосов
/ 14 мая 2010

Я хотел бы определить как класс X с помощью статического метода:

class X
{
 static string get_type () {return "X";}
 //other virtual methods
}

Я хотел бы заставить классы, которые наследуются от X, переопределить метод get_type () и возвращать строки, отличные от «X» (я рад, если они просто переопределят get_type сейчас).

Как мне это сделать? Я знаю, что у меня не может быть виртуальных статических методов.

Edit: вопрос не о type_id, а вообще о статическом методе, который должен быть переопределен Например

class X {
 static int getid() {return 1;}
}

Ответы [ 10 ]

5 голосов
/ 14 мая 2010
template<int id>
class X {
public:
    static int getid() { return id; }
};

class Y : public X<2> {
};

Вы не переопределили метод, но вы заставили каждый подкласс предоставлять идентификатор. Предостережение: я не пробовал это, может быть, есть какая-то тонкая причина, по которой это не сработает.

3 голосов
/ 14 мая 2010

Если я не ошибаюсь, чтобы вызвать статический метод, вы должны вызвать метод, указав точное имя класса, например, X::get_type();, DerivedClass::get_type() и т. Д. И в любом случае, если он вызывается для объекта динамический тип объекта не учитывается. Так что, по крайней мере, в конкретном случае, это может быть полезно только в шаблонном контексте, когда вы не ожидаете полиморфного поведения.

Однако я не понимаю, почему нельзя заставить каждый интересующий класс (наследуемый или нет, так как «полиморфизм во время компиляции» не заботится) предоставить эту функциональность с помощью шаблонов. В следующем случае вы должны специализировать функцию get_type, иначе у вас будет ошибка времени компиляции:

#include <string>

struct X {}; 
struct Derived: X {};

template <class T> std::string get_type() {
    static_assert(sizeof(T) == 0, "get_type not specialized for given type");
    return std::string(); 
}

template <> std::string get_type<X>() {
    return "X"; 
}

int main() {
    get_type<X>();
    get_type<Derived>(); //error 
}

(static_assert - это C ++ 0x, в противном случае используйте вашу любимую реализацию, например, BOOST_STATIC_ASSERT. А если вам не нравится специализация функций, вместо этого специализируйте структуру. И если вы хотите вызвать ошибку, если кто-то случайно попытается специализировать его для типов, не производных от X, тогда это также должно быть возможно с type_traits.)

2 голосов
/ 14 мая 2010

Я бы сказал, что вы знаете почему, но на всякий случай вот хорошее объяснение:

http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=/com.ibm.xlcpp8l.doc/language/ref/cplr139.htm

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

1 голос
/ 29 августа 2010

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

Люди часто выражают факты , используя статические классы и / или члены - то, что верно для всех случаев, если экземпляры актуальны, или просто факты о мире в случае статических классов. Предположим, вы моделируете урок философии. Вы можете определить abstract class Theory, чтобы представить теорию, которую нужно преподавать, затем наследовать от Theory в TheoryOfSelf, TheoryOfMind и так далее. Чтобы преподавать Теорию, вам действительно нужен метод с именем express(), который выражает теорию с использованием определенного поворота фразы, подходящего для аудитории. Можно предположить, что любой наследующий класс должен предоставлять идентичный метод express(). Если бы я был в состоянии, я бы смоделировал это отношение, используя static virtual Theory.express() - это и констатация факта, выходящая за рамки концепции экземпляров (следовательно, статических), и неспецифическая, требующая конкретной реализации каждым типом теории (следовательно, виртуальный).

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

Лучшее решение этой проблемы, о котором я только мог подумать, - это вместо этого моделировать Theory как одиночный - может быть экземпляр теории, но есть только одна из них , Если вам нужна альтернатива, это другой тип, поэтому создайте новый производный класс. Для меня этот подход просто кажется произвольным и вносит ненужный шум.

1 голос
/ 15 мая 2010

Используйте Delphi, он поддерживает виртуальные статические члены на классах. ;>

1 голос
/ 15 мая 2010

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

class Base {
  static std::vector<std::pair<const std::type_info*, int> > datas;
  typedef std::vector<std::pair<const std::type_info*, int> >::iterator iterator;
public:
  virtual ~Base() { }
  int Data() const {
    const std::type_info& info = typeid(*this);
    for(iterator i = datas.begin(); i != datas.end(); ++i)
      if(*(i->first) == info) return i->second;
    throw "Unregistered Type";
  }

  static bool RegisterClass(const Base& p, int data) {
    const std::type_info& info = typeid(p);
    for(iterator i = datas.begin(); i != datas.end(); ++i) {
      if(i->second == data) {
    if(*(i->first) != info) throw "Duplicate Data";
    return true;
      }
      if(*(i->first) == info) throw "Reregistering";
    }
    datas.push_back(std::make_pair(&info, data));
    return true;
  }
};
std::vector<std::pair<const std::type_info*, int> > Base::datas;

class Derived : public Base { };
const DerivedRegisterFlag = Base::RegisterClass(Derived(), 10);

class OtherDerived : public Base { };
const OtherDerivedRegisterFlag = Base::RegisterClass(OtherDerived(), 10); //exception

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

Я выбрал неупорядоченный вектор для простоты; Я не уверен, что type_info::before обеспечивает необходимую семантику, которая будет использоваться в качестве предиката для карты, и, вероятно, у вас не будет столько производных классов, что линейный поиск будет проблематичным в любом случае. Я храню указатель, потому что вы не можете скопировать type_info объекты напрямую. Это в основном безопасно, поскольку время жизни объекта, возвращаемого typeid, составляет всю программу. Могут быть проблемы, когда программа закрывается, я не уверен.

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

Наконец, нет, это не статично, но «статический» и «виртуальный» в любом случае не имеют смысла вместе. Если у вас нет экземпляра типа для действия, то как вы узнаете, какой перезаписанный метод выбрать? Есть несколько случаев с шаблонами, когда вы могли бы на законных основаниях хотеть вызвать статический метод без реального объекта, но это вряд ли будет распространено.

* edit: Кроме того, я не уверен, как это взаимодействует с динамически связанными библиотеками и тому подобным. Я подозреваю, что RTTI ненадежен в таких ситуациях, поэтому, очевидно, это так же ненадежно.

1 голос
/ 14 мая 2010

Вот, пожалуйста,

class X
{
  static string get_type() {return "X"; }
};

class Y : public X
{
  static string get_type() {return "Y"; }
};

Приведенный выше код делает именно то, что вы просили: производный класс переопределяет get_type и возвращает другую строку. Если это не то, что вы хотите, вы должны объяснить, почему. Вы должны объяснить, что вы пытаетесь сделать и какое поведение вы ожидаете от этого статического метода. Если абсолютно неясно, сформулируйте свой оригинальный вопрос.

1 голос
/ 14 мая 2010

Вы не можете сделать это по ряду причин. Вы не можете определить функцию в X, и она должна быть чисто виртуальной. Вы не можете иметь виртуальные статические функции вообще.

Почему они должны быть статичными?

1 голос
/ 14 мая 2010

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

1 голос
/ 14 мая 2010

Не делайте этого, используйте typeid.

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