Статические виртуальные элементы C ++?
Возможно ли в C ++ иметь функцию-член, которая является как static
и virtual
? По-видимому, нет простого способа сделать это ( static virtual member();
это ошибка компиляции), но есть ли хотя бы один способ добиться такого же эффекта?
IE:
struct Object { struct TypeInformation; static virtual const TypeInformation &GetTypeInformation() const; }; struct SomeObject : public Object { static virtual const TypeInformation &GetTypeInformation() const; };
Имеет смысл использовать GetTypeInformation()
как на экземпляре ( object->GetTypeInformation()
), так и на classе ( SomeObject::GetTypeInformation()
), который может быть полезен для сравнения и жизненно важного для шаблонов.
- Как обслуживать статические файлы из другого каталога, чем статический путь?
- Изменение частного статического конечного поля с использованием отражения Java
- Статический вложенный class в Java, почему?
- Почему все поля в интерфейсе неявно статичны и окончательны?
- Вызывает статические методы через объект «плохая форма»? Зачем?
Единственные способы, которыми я могу думать, – это написать две функции / функцию и константу, для каждого classа или использовать macros.
Любые другие решения?
- Использование инициализаторов и конструкторов в Java
- Почему class System.Random не статичен?
- Порядок инициализации статических переменных
- Из статической библиотеки MinGW (.a) в статическую библиотеку Visual Studio (.lib)
- Статические правила сериализации Java?
- Возвращает указатель на статическую локальную переменную?
- Является ли порядок инициализации статического classа в C # детерминированным?
- В чем разница между функцией static func и class func в Swift?
Нет, нет способа сделать это, так как произойдет, когда вы Object::GetTypeInformation()
? Он не может знать, какую версию производного classа нужно вызвать, поскольку с ним нет связанного с ним объекта.
Вам нужно будет сделать его нестатической виртуальной функцией для правильной работы; если вы также хотите иметь возможность вызывать версию определенного производного classа практически без экземпляра объекта, вам также придется предоставить вторую старую не виртуальную версию.
Многие говорят, что это невозможно, я бы сделал еще один шаг и сказал, что это не имеет смысла.
Статический член – это то, что не относится ни к одному экземпляру, только к classу.
Виртуальный член – это то, что не относится непосредственно к любому classу, только к экземпляру.
Таким образом, статический виртуальный член будет тем, что не относится ни к какому экземпляру или к любому classу.
Я столкнулся с этой проблемой на днях: у меня было несколько classов, заполненных статическими методами, но я хотел использовать наследование и виртуальные методы и сократить повторение кода. Мое решение было:
Вместо использования статических методов используйте синглтон с виртуальными методами.
Другими словами, каждый class должен содержать статический метод, который вы вызываете, чтобы получить указатель на один, общий экземпляр classа. Вы можете сделать истинные конструкторы частными или защищенными, чтобы внешний код не мог злоупотреблять им, создавая дополнительные экземпляры.
На практике использование singleton очень похоже на использование статических методов, за исключением того, что вы можете использовать наследование и виртуальные методы.
Возможно!
Но что именно возможно, давайте сужим. Люди часто хотят какой-то «статической виртуальной функции» из-за дублирования кода, необходимого для того, чтобы вызывать одну и ту же функцию через статический вызов «SomeDerivedClass :: myfunction ()» и полиморфный вызов «base_class_pointer-> myfunction ()». «Юридическим» методом разрешения таких функций является дублирование определений функций:
class Object { public: static string getTypeInformationStatic() { return "base class";} virtual string getTypeInformation() { return getTypeInformationStatic(); } }; class Foo: public Object { public: static string getTypeInformationStatic() { return "derived class";} virtual string getTypeInformation() { return getTypeInformationStatic(); } };
Что делать, если базовый class имеет большое количество статических функций, и производный class должен переопределить каждый из них, и один забыл предоставить дублирующее определение для виртуальной функции. Правильно, во время выполнения мы получим странную ошибку, которую трудно отследить. Причина дублирования кода – это плохо. Следующее пытается решить эту проблему (и я хочу заранее сказать, что она полностью безопасна для текста и не содержит черной магии, такой как typeid или dynamic_cast 🙂
Итак, мы хотим предоставить только одно определение getTypeInformation () для производного classа, и очевидно, что это должно быть определение статической функции, потому что невозможно вызвать «SomeDerivedClass :: getTypeInformation ()», если getTypeInformation () виртуальная. Как мы можем вызвать статическую функцию производного classа через указатель на базовый class? Это невозможно с помощью vtable, поскольку vtable хранит указатели только на виртуальные функции, и поскольку мы решили не использовать виртуальные функции, мы не можем изменить vtable для нашей выгоды. Затем, чтобы иметь доступ к статической функции для производного classа с помощью указателя на базовый class, мы должны каким-то образом сохранить тип объекта в его базовом classе. Один из подходов состоит в том, чтобы сделать базовый class темплатированным с использованием «любопытно повторяющегося шаблона шаблона», но здесь это не подходит, и мы будем использовать метод «стирание типа»:
class TypeKeeper { public: virtual string getTypeInformation() = 0; }; template class TypeKeeperImpl: public TypeKeeper { public: virtual string getTypeInformation() { return T::getTypeInformationStatic(); } };
Теперь мы можем сохранить тип объекта в базовом classе «Объект» с переменной «хранитель»:
class Object { public: Object(){} boost::scoped_ptr keeper; //not virtual string getTypeInformation() const { return keeper? keeper->getTypeInformation(): string("base class"); } };
В производном classе хранитель должен быть инициализирован во время построения:
class Foo: public Object { public: Foo() { keeper.reset(new TypeKeeperImpl()); } //note the name of the function static string getTypeInformationStatic() { return "class for proving static virtual functions concept"; } };
Давайте добавим синтаксический сахар:
template void override_static_functions(T* t) { t->keeper.reset(new TypeKeeperImpl()); } #define OVERRIDE_STATIC_FUNCTIONS override_static_functions(this)
Теперь объявления потомков выглядят так:
class Foo: public Object { public: Foo() { OVERRIDE_STATIC_FUNCTIONS; } static string getTypeInformationStatic() { return "class for proving static virtual functions concept"; } }; class Bar: public Foo { public: Bar() { OVERRIDE_STATIC_FUNCTIONS; } static string getTypeInformationStatic() { return "another class for the same reason"; } };
Применение:
Object* obj = new Foo(); cout << obj->getTypeInformation() << endl; //calls Foo::getTypeInformationStatic() obj = new Bar(); cout << obj->getTypeInformation() << endl; //calls Bar::getTypeInformationStatic() Foo* foo = new Bar(); cout << foo->getTypeInformation() << endl; //calls Bar::getTypeInformationStatic() Foo::getTypeInformation(); //compile-time error Foo::getTypeInformationStatic(); //calls Foo::getTypeInformationStatic() Bar::getTypeInformationStatic(); //calls Bar::getTypeInformationStatic()
Преимущества:
- меньше дублирования кода (но мы должны вызывать OVERRIDE_STATIC_FUNCTIONS в каждом конструкторе)
Недостатки:
- OVERRIDE_STATIC_FUNCTIONS в каждом конструкторе
- память и производительность
- повышенная сложность
Открытые вопросы:
1) Существуют ли разные имена для статических и виртуальных функций, как решить проблему неопределенности здесь?
class Foo { public: static void f(bool f=true) { cout << "static";} virtual void f() { cout << "virtual";} }; //somewhere Foo::f(); //calls static f(), no ambiguity ptr_to_foo->f(); //ambiguity
2) как неявно вызывать OVERRIDE_STATIC_FUNCTIONS внутри каждого конструктора?
Возможно. Сделайте две функции: статические и виртуальные
struct Object{ struct TypeInformation; static const TypeInformation &GetTypeInformationStatic() const { return GetTypeInformationMain1(); } virtual const TypeInformation &GetTypeInformation() const { return GetTypeInformationMain1(); } protected: static const TypeInformation &GetTypeInformationMain1(); // Main function }; struct SomeObject : public Object { static const TypeInformation &GetTypeInformationStatic() const { return GetTypeInformationMain2(); } virtual const TypeInformation &GetTypeInformation() const { return GetTypeInformationMain2(); } protected: static const TypeInformation &GetTypeInformationMain2(); // Main function };
В то время как Alsk уже дал довольно подробный ответ, я хотел бы добавить альтернативу, так как я думаю, что его расширенная реализация сложна.
Начнем с абстрактного базового classа, который предоставляет интерфейс для всех типов объектов:
class Object { public: virtual char* GetClassName() = 0; };
Теперь нам нужна реальная реализация. Но чтобы избежать необходимости писать как статические, так и виртуальные методы, мы будем иметь на самом деле classы объектов, наследующие виртуальные методы. Это, очевидно, работает только в том случае, если базовый class знает, как получить доступ к статической функции-члену. Поэтому нам нужно использовать шаблон и передать ему имя classа объектов:
template class ObjectImpl : public Object { public: virtual char* GetClassName() { return ObjectType::GetClassNameStatic(); } };
Наконец, нам нужно реализовать наш реальный объект (ы). Здесь нам нужно только реализовать статическую функцию-член, функции виртуальных членов будут наследоваться из classа шаблона ObjectImpl, созданного с именем производного classа, поэтому он получит доступ к его статическим членам.
class MyObject : public ObjectImpl { public: static char* GetClassNameStatic() { return "MyObject"; } }; class YourObject : public ObjectImpl { public: static char* GetClassNameStatic() { return "YourObject"; } };
Давайте добавим код для тестирования:
char* GetObjectClassName(Object* object) { return object->GetClassName(); } int main() { MyObject myObject; YourObject yourObject; printf("%s\n", MyObject::GetClassNameStatic()); printf("%s\n", myObject.GetClassName()); printf("%s\n", GetObjectClassName(&myObject)); printf("%s\n", YourObject::GetClassNameStatic()); printf("%s\n", yourObject.GetClassName()); printf("%s\n", GetObjectClassName(&yourObject)); return 0; }
Нет, это невозможно, потому что у статических функций-членов отсутствует указатель. И статические члены (обе функции и переменные) на самом деле не являются членами classа. Они просто вызывают ClassName::member
и придерживаются спецификаторов доступа к classу. Их хранение определяется где-то вне classа; хранилище не создается каждый раз, когда вы создаете объект classа. Указатели на члены classа являются особыми в семантике и синтаксисе. Указатель на статический член является нормальным указателем во всех отношениях.
виртуальные функции в classе нуждаются в this
указателе и очень связаны с classом, поэтому они не могут быть статическими.
Ну, довольно поздний ответ, но это возможно с использованием любопытно повторяющегося шаблона шаблона. В этой статье в википедии есть информация, которая вам нужна, а также пример под статическим polymorphismом – это то, о чем вас просят.
Нет, функция статического члена не может быть виртуальной. Поскольку виртуальная концепция разрешается во время выполнения с помощью vptr, а vptr является нестационарным членом classа. Таким образом, статическая функция-член не может активировать vptr, поэтому статический член может виртуальный.
Я думаю, что вы пытаетесь сделать это через шаблоны. Я пытаюсь читать между строк здесь. То, что вы пытаетесь сделать, это вызвать метод из некоторого кода, где он вызывает производную версию, но вызывающий не указывает какой class. Пример:
class Foo { public: void M() {...} }; class Bar : public Foo { public: void M() {...} }; void Try() { xxx::M(); } int main() { Try(); }
Вы хотите, чтобы Try () вызывал Bar версию M без указания Bar. То, как вы делаете это для статики, – это использовать шаблон. Так измените его так:
class Foo { public: void M() {...} }; class Bar : public Foo { public: void M() {...} }; template void Try() { T::M(); } int main() { Try(); }
Нет, это невозможно, поскольку статические члены привязаны во время компиляции, а виртуальные члены связаны во время выполнения.
Во-первых, ответы правильны, что запрос OP является противоречием в терминах: виртуальные методы зависят от типа времени выполнения экземпляра; статические функции специально не зависят от экземпляра – только от типа. Тем не менее, имеет смысл статические функции возвращать что-то конкретное для типа. Например, у меня было семейство classов MouseTool для шаблона State, и я начал с того, что у каждого из них была статическая функция, возвращающая модификатор клавиатуры, который шел с ним; Я использовал эти статические функции в заводской функции, которые сделали правильный экземпляр MouseTool. Эта функция проверила состояние мыши на MouseToolA :: keyboardModifier (), MouseToolB :: keyboardModifier () и т. Д., А затем создала соответствующий экземпляр. Конечно, позже мне захотелось проверить правильность состояния, поэтому я хотел написать что-то вроде «if (keyboardModifier == dynamic_type (* state) :: keyboardModifier ())» (не настоящий синтаксис C ++), и именно этот вопрос задает вопрос ,
Итак, если вы захотите этого, вы можете захотеть создать свое решение. Тем не менее, я понимаю желание иметь статические методы, а затем динамически их называть динамическим типом экземпляра. Я думаю, что шаблон посетителя может дать вам то, что вы хотите. Это дает вам то, что вы хотите. Это немного лишний код, но он может быть полезен другим посетителям.
См. http://en.wikipedia.org/wiki/Visitor_pattern для фона.
struct ObjectVisitor; struct Object { struct TypeInformation; static TypeInformation GetTypeInformation(); virtual void accept(ObjectVisitor& v); }; struct SomeObject : public Object { static TypeInformation GetTypeInformation(); virtual void accept(ObjectVisitor& v) const; }; struct AnotherObject : public Object { static TypeInformation GetTypeInformation(); virtual void accept(ObjectVisitor& v) const; };
Затем для каждого конкретного объекта:
void SomeObject::accept(ObjectVisitor& v) const { v.visit(*this); // The compiler statically picks the visit method based on *this being a const SomeObject&. } void AnotherObject::accept(ObjectVisitor& v) const { v.visit(*this); // Here *this is a const AnotherObject& at compile time. }
а затем определить базового посетителя:
struct ObjectVisitor { virtual ~ObjectVisitor() {} virtual void visit(const SomeObject& o) {} // Or = 0, depending what you feel like. virtual void visit(const AnotherObject& o) {} // Or = 0, depending what you feel like. // More virtual void visit() methods for each Object class. };
Затем конкретный посетитель, который выбирает соответствующую статическую функцию:
struct ObjectVisitorGetTypeInfo { Object::TypeInformation result; virtual void visit(const SomeObject& o) { result = SomeObject::GetTypeInformation(); } virtual void visit(const AnotherObject& o) { result = AnotherObject::GetTypeInformation(); } // Again, an implementation for each concrete Object. };
наконец, используйте его:
void printInfo(Object& o) { ObjectVisitorGetTypeInfo getTypeInfo; Object::TypeInformation info = o.accept(getTypeInfo).result; std::cout << info << std::endl; }
Заметки:
- Констанция осталась как упражнение.
- Вы вернули ссылку из статики. Если у вас нет сингла, это сомнительно.
Если вы хотите избежать ошибок копирования-вставки, когда один из ваших методов посещения вызывает неправильную статическую функцию, вы можете использовать шаблонную вспомогательную функцию (которая не может быть виртуальной) вашим посетителем с таким шаблоном:
struct ObjectVisitorGetTypeInfo { Object::TypeInformation result; virtual void visit(const SomeObject& o) { doVisit(o); } virtual void visit(const AnotherObject& o) { doVisit(o); } // Again, an implementation for each concrete Object. private: template void doVisit(const T& o) { result = T::GetTypeInformation(); } };
Возможно, вы можете попробовать мое решение ниже:
class Base { public: Base(void); virtual ~Base(void); public: virtual void MyVirtualFun(void) = 0; static void MyStaticFun(void) { assert( mSelf != NULL); mSelf->MyVirtualFun(); } private: static Base* mSelf; }; Base::mSelf = NULL; Base::Base(void) { mSelf = this; } Base::~Base(void) { // please never delete mSelf or reset the Value of mSelf in any deconstructors } class DerivedClass : public Base { public: DerivedClass(void) : Base() {} ~DerivedClass(void){} public: virtual void MyVirtualFun(void) { cout<<"Hello, it is DerivedClass!"<
Как говорили другие, есть две важные части информации:
-
this
указатель отсутствует при выполнении статического вызова функции и -
this
указатель указывает на структуру, в которой виртуальная таблица или thunk используются для поиска того, какой метод времени выполнения для вызова.
Статическая функция определяется во время компиляции.
Я показал этот пример кода в статических членах C ++ в classе ; он показывает, что вы можете вызвать статический метод с указанием нулевого указателя:
struct Foo { static int boo() { return 2; } }; int _tmain(int argc, _TCHAR* argv[]) { Foo* pFoo = NULL; int b = pFoo->boo(); // b will now have the value 2 return 0; }