Дизайн C++ со статическими методами

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

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

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

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

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

class X {
 static int getid() {return 1;}
}
5
задан user231536 14 May 2010 в 18:57
поделиться

9 ответов

Вот так

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

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

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

1
ответ дан 18 December 2019 в 09:48
поделиться

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

1
ответ дан 18 December 2019 в 09:48
поделиться

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

1
ответ дан 18 December 2019 в 09:48
поделиться
template<int id>
class X {
public:
    static int getid() { return id; }
};

class Y : public X<2> {
};

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

5
ответ дан 18 December 2019 в 09:48
поделиться

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

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

1
ответ дан 18 December 2019 в 09:48
поделиться

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

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

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

2
ответ дан 18 December 2019 в 09:48
поделиться

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

1
ответ дан 18 December 2019 в 09:48
поделиться

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

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
ответ дан 18 December 2019 в 09:48
поделиться

Если я не ошибаюсь, чтобы вызвать статический метод, вы должны вызвать метод, указав точное имя класса, например.g 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 is C ++ 0x, в противном случае используйте вашу любимую реализацию , например, BOOST_STATIC_ASSERT . И если вам не нравится специализация функций, вместо этого специализируйте структуру. И если вы хотите вызвать ошибку, если кто-то случайно попытается специализировать ее для типов, не производных от X, тогда это также должно быть возможно с type_traits .)

3
ответ дан 18 December 2019 в 09:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: