Возможно ли в 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()
как в instance ( object->GetTypeInformation()
), так и в классе ( SomeObject::GetTypeInformation()
), что может быть полезно для сравнений и жизненно важно для шаблонов.
Единственные способы, которые я могу придумать, - это написать две функции / функцию и константу для каждого класса или использовать макросы.
Какие-нибудь другие решения?
const
в сигнатуре метода помечается неявныйthis
указатель как константа, и он не может применяться к статическим методам, поскольку у них отсутствует неявный параметр.Ответы:
Нет, нет возможности сделать это, ведь что будет, когда вы позвоните
Object::GetTypeInformation()
? Он не может знать, какую версию производного класса вызывать, поскольку с ним не связан ни один объект.Вам нужно будет сделать его нестатической виртуальной функцией, чтобы она работала правильно; если вы также хотите иметь возможность вызывать версию определенного производного класса не виртуально без экземпляра объекта, вам также необходимо предоставить вторую избыточную статическую невиртуальную версию.
источник
Многие говорят, что это невозможно, я бы пошел еще дальше и сказал, что это не имеет смысла.
Статический член - это то, что не относится ни к какому экземпляру, а только к классу.
Виртуальный член - это то, что не относится напрямую ни к какому классу, а только к экземпляру.
Таким образом, статический виртуальный член будет чем-то, что не относится ни к какому экземпляру или какому-либо классу.
источник
static virtual
метода, ноstatic
чистыйvirtual
метод очень важен для интерфейса.На днях я столкнулся с этой проблемой: у меня было несколько классов, полных статических методов, но я хотел использовать наследование и виртуальные методы и уменьшить повторение кода. Мое решение было:
Вместо использования статических методов используйте синглтон с виртуальными методами.
Другими словами, каждый класс должен содержать статический метод, который вы вызываете для получения указателя на единственный общий экземпляр класса. Вы можете сделать настоящие конструкторы частными или защищенными, чтобы внешний код не мог их неправильно использовать, создав дополнительные экземпляры.
На практике использование синглтона очень похоже на использование статических методов, за исключением того, что вы можете воспользоваться преимуществами наследования и виртуальных методов.
источник
Это возможно!
Но что именно возможно, давайте сузим круг вопросов. Людям часто нужна какая-то «статическая виртуальная функция» из-за дублирования кода, необходимого для возможности вызова одной и той же функции посредством статического вызова «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(); } };
Что делать, если базовый класс имеет большое количество статических функций, а производный класс должен переопределять каждую из них, а один забыл предоставить повторяющееся определение для виртуальной функции. Хорошо, мы получим странную ошибку во время выполнения, которую трудно отследить. Причина дублирования кода - это плохо. Следующее пытается решить эту проблему (и я хочу заранее сказать, что он полностью типобезопасен и не содержит черной магии, такой как typeid или dynamic_cast :)
Итак, мы хотим предоставить только одно определение getTypeInformation () для каждого производного класса, и очевидно, что это должно быть определение статическогофункция, потому что невозможно вызвать SomeDerivedClass :: getTypeInformation (), если getTypeInformation () является виртуальным. Как мы можем вызвать статическую функцию производного класса через указатель на базовый класс? Это невозможно с vtable, потому что vtable хранит указатели только на виртуальные функции, и, поскольку мы решили не использовать виртуальные функции, мы не можем изменять vtable в наших интересах. Затем, чтобы иметь возможность получить доступ к статической функции для производного класса через указатель на базовый класс, мы должны каким-то образом сохранить тип объекта в его базовом классе. Один из подходов состоит в том, чтобы сделать базовый класс шаблонным с использованием «любопытно повторяющегося шаблона шаблона», но здесь он не подходит, и мы будем использовать технику, называемую «стирание типа»:
class TypeKeeper { public: virtual string getTypeInformation() = 0; }; template<class T> class TypeKeeperImpl: public TypeKeeper { public: virtual string getTypeInformation() { return T::getTypeInformationStatic(); } };
Теперь мы можем сохранить тип объекта в базовом классе Object с переменной keeper:
class Object { public: Object(){} boost::scoped_ptr<TypeKeeper> keeper; //not virtual string getTypeInformation() const { return keeper? keeper->getTypeInformation(): string("base class"); } };
В производном классе хранитель должен быть инициализирован во время построения:
class Foo: public Object { public: Foo() { keeper.reset(new TypeKeeperImpl<Foo>()); } //note the name of the function static string getTypeInformationStatic() { return "class for proving static virtual functions concept"; } };
Добавим синтаксический сахар:
template<class T> void override_static_functions(T* t) { t->keeper.reset(new TypeKeeperImpl<T>()); } #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()
Преимущества:
Недостатки:
Открытые вопросы:
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 внутри каждого конструктора?
источник
Хотя Альск уже дал довольно подробный ответ, я хотел бы добавить альтернативу, так как считаю его расширенную реализацию слишком сложной.
Начнем с абстрактного базового класса, который предоставляет интерфейс для всех типов объектов:
class Object { public: virtual char* GetClassName() = 0; };
Теперь нам нужна реальная реализация. Но чтобы избежать написания как статических, так и виртуальных методов, мы сделаем так, чтобы наши фактические классы объектов наследовали виртуальные методы. Очевидно, это работает только в том случае, если базовый класс знает, как получить доступ к статической функции-члену. Поэтому нам нужно использовать шаблон и передать ему фактическое имя класса объектов:
template<class ObjectType> class ObjectImpl : public Object { public: virtual char* GetClassName() { return ObjectType::GetClassNameStatic(); } };
Наконец, нам нужно реализовать наш реальный объект (ы). Здесь нам нужно реализовать только статическую функцию-член, виртуальные функции-члены будут унаследованы от класса шаблона ObjectImpl, созданного с именем производного класса, поэтому он получит доступ к его статическим членам.
class MyObject : public ObjectImpl<MyObject> { public: static char* GetClassNameStatic() { return "MyObject"; } }; class YourObject : public ObjectImpl<YourObject> { 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; }
Приложение (12 января 2019 г.):
Вместо использования функции GetClassNameStatic () вы также можете определить имя класса как статический член, даже «встроенный», который IIRC работает с C ++ 11 (не пугайтесь всех модификаторов :)):
class MyObject : public ObjectImpl<MyObject> { public: // Access this from the template class as `ObjectType::s_ClassName` static inline const char* const s_ClassName = "MyObject"; // ... };
источник
Это возможно. Сделайте две функции: статическую и виртуальную
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 };
источник
Нет, это невозможно, потому что у статических функций-членов отсутствует
this
указатель. И статические члены (как функции, так и переменные) сами по себе не являются членами класса. Они просто вызываютсяClassName::member
спецификаторами доступа к классам и придерживаются их. Их хранилище определено где-то вне класса; хранилище не создается каждый раз, когда вы создаете экземпляр объекта класса. Указатели на члены класса имеют особую семантику и синтаксис. Указатель на статический член - это нормальный указатель во всех отношениях.виртуальным функциям в классе нужен
this
указатель, и они очень связаны с классом, поэтому они не могут быть статическими.источник
this
Указатель нужен только нестатическим функциям . статические функции не относятся к экземпляру и не нуждаются в этом. Итак - это не причина, по которой виртуальные статические члены невозможны.Что ж, довольно поздний ответ, но можно использовать любопытно повторяющийся шаблон шаблона. В этой статье в Википедии есть необходимая информация, а также пример статического полиморфизма - это то, что вас просят.
источник
Я думаю, что то, что вы пытаетесь сделать, можно сделать с помощью шаблонов. Я пытаюсь читать здесь между строк. Вы пытаетесь вызвать метод из некоторого кода, где он вызывает производную версию, но вызывающий не указывает, какой класс. Пример:
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 <class T> void Try() { T::M(); } int main() { Try<Bar>(); }
источник
Нет, статическая функция-член не может быть виртуальной. Поскольку виртуальная концепция разрешается во время выполнения с помощью vptr, а vptr не является статическим членом класса. Из-за этой статической функции-члена не может получить доступ к vptr, поэтому статический член может не виртуальный.
источник
Это невозможно, но это просто упущение. Это не то, что «не имеет смысла», как, кажется, утверждают многие люди. Для ясности, я говорю примерно о таком:
struct Base { static virtual void sayMyName() { cout << "Base\n"; } }; struct Derived : public Base { static void sayMyName() override { cout << "Derived\n"; } }; void foo(Base *b) { b->sayMyName(); Derived::sayMyName(); // Also would work. }
Это на 100% то, что можно реализовать (а этого просто нет), и я бы сказал, что это полезно.
Рассмотрим, как работают обычные виртуальные функции. Удалите
static
s и добавьте еще кое-что, и мы получим:struct Base { virtual void sayMyName() { cout << "Base\n"; } virtual void foo() { } int somedata; }; struct Derived : public Base { void sayMyName() override { cout << "Derived\n"; } }; void foo(Base *b) { b->sayMyName(); }
Это работает нормально, и в основном происходит то, что компилятор создает две таблицы, называемые VTables, и назначает индексы виртуальным функциям, подобным этой.
enum Base_Virtual_Functions { sayMyName = 0; foo = 1; }; using VTable = void*[]; const VTable Base_VTable = { &Base::sayMyName, &Base::foo }; const VTable Derived_VTable = { &Derived::sayMyName, &Base::foo };
Затем каждый класс с виртуальными функциями дополняется другим полем, указывающим на его VTable, поэтому компилятор в основном меняет их так:
struct Base { VTable* vtable; virtual void sayMyName() { cout << "Base\n"; } virtual void foo() { } int somedata; }; struct Derived : public Base { VTable* vtable; void sayMyName() override { cout << "Derived\n"; } };
Тогда что на самом деле происходит, когда вы звоните
b->sayMyName()
? В основном это:(Первый параметр становится
this
.)Хорошо, хорошо, как бы он работал со статическими виртуальными функциями? Ну, в чем разница между статическими и нестатическими функциями-членами? Единственное отличие в том, что последние получают
this
указатель.Мы можем сделать то же самое со статическими виртуальными функциями - просто удалите
this
указатель.Тогда это может поддерживать оба синтаксиса:
b->sayMyName(); // Prints "Base" or "Derived"... Base::sayMyName(); // Always prints "Base".
Так что игнорируйте всех скептиков. В этом есть смысл. Почему тогда не поддерживается? Я думаю, это потому, что от этого мало пользы и может даже немного сбить с толку.
Единственное техническое преимущество по сравнению с обычной виртуальной функцией заключается в том, что вам не нужно переходить
this
к функции, но я не думаю, что это окажет заметное влияние на производительность.Это означает, что у вас нет отдельной статической и нестатической функции для случаев, когда у вас есть экземпляр, и когда у вас нет экземпляра, но также может сбивать с толку тот факт, что он действительно «виртуальный» только тогда, когда вы используете вызов экземпляра.
источник
Нет, это невозможно, поскольку статические члены привязываются во время компиляции, а виртуальные - во время выполнения.
источник
Во-первых, ответы верны: то, что запрашивает OP, противоречит терминам: виртуальные методы зависят от типа времени выполнения экземпляра; статические функции конкретно не зависят от экземпляра - только от типа. Тем не менее, имеет смысл, чтобы статические функции возвращали что-то конкретное для типа. Например, у меня было семейство классов MouseTool для паттерна State, и я начал использовать в каждом из них статическую функцию, возвращающую соответствующий модификатор клавиатуры; Я использовал эти статические функции в фабричной функции, которая создала правильный экземпляр MouseTool. Эта функция проверила состояние мыши по MouseToolA :: keyboardModifier (), MouseToolB :: keyboardModifier () и т. Д., А затем создала соответствующий экземпляр. Конечно, позже я хотел проверить, правильное ли состояние, поэтому я хотел написать что-то вроде "
Итак, если вы обнаружите, что хотите этого, вы можете пересмотреть свое решение. Тем не менее, я понимаю желание иметь статические методы, а затем вызывать их динамически в зависимости от динамического типа экземпляра. Я думаю, что шаблон посетителя может дать вам то, что вы хотите. Это дает вам то, что вы хотите. Это немного лишний код, но он может быть полезен другим посетителям.
См .: 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 <typename T> void doVisit(const T& o) { result = T::GetTypeInformation(); } };
источник
Foo foo; ... foo::bar();
вместоFoo::bar();
). Это мало чем отличается,decltype(foo)::bar();
но это снова будет статически привязано. Подход посетителя кажется разумным способом добиться такого поведения, не делая статический метод просто виртуальным константным методом.В C ++ вы можете использовать статическое наследование с помощью метода crt. Например, он широко используется в шаблоне окна atl & wtl.
См. Https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern.
Проще говоря, у вас есть класс, созданный по шаблону, например class myclass: public myancestor. С этого момента класс myancestor может теперь вызывать вашу статическую функцию T :: YourImpl.
источник
Возможно, вы можете попробовать мое решение ниже:
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!"<<endl; } }; int main() { DerivedClass testCls; testCls.MyStaticFun(); //correct way to invoke this kind of static fun DerivedClass::MyStaticFun(); //wrong way return 0; }
источник
Base::mSelf
ссылается на САМЫЙ НЕДАВНО созданный экземпляр любого производного класса, даже если этот экземпляр был уничтожен . такclass D1 : public Base ...; class D2 : public Base ...; ...; D1* pd1 = new D1(); D2* pd2 = new D2(); pd1->MyStaticFun(); /* calls D2::MyVirtualFun() */ delete pd2; pd1->MyStaticFun(); /* calls via deleted pd2 */
что это НЕ то, что нужно.Как уже говорили другие, есть 2 важных элемента информации:
this
указателя при вызове статической функции иthis
указывает указатель на структуру , где виртуальная таблица, или преобразователь, которые используются для просмотра , какой метод выполнения для вызова.Статическая функция определяется во время компиляции.
Я показал этот пример кода в статических членах класса C ++ ; он показывает, что вы можете вызвать статический метод с помощью нулевого указателя:
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; }
источник
p < null
, иp >= null
т.д., все также не определены.