C ++静态虚拟成员?
有可能在C ++中有一个static
和virtual
的成员函数? 显然,没有一个简单的方法来做到这一点( static virtual member();
是一个编译错误),但至less有一种方法来实现相同的效果?
IE:
struct Object { struct TypeInformation; static virtual const TypeInformation &GetTypeInformation() const; }; struct SomeObject : public Object { static virtual const TypeInformation &GetTypeInformation() const; };
在一个实例( object->GetTypeInformation()
)和一个类( SomeObject::GetTypeInformation()
)上使用SomeObject::GetTypeInformation()
是有意义的,这对于比较和对模板来说是非常重要的。
我能想到的唯一方法就是每个类编写两个函数/一个函数和一个常量,或者使用macros。
任何其他解决scheme
不,没有办法去做,因为当你调用Object::GetTypeInformation()
时会发生什么? 由于没有与之关联的对象,因此无法知道要调用哪个派生类版本。
你必须使它成为一个非静态的虚函数才能正常工作。 如果您还希望能够在没有对象实例的情况下非虚拟地调用特定派生类的版本,则还必须提供第二个冗余静态非虚拟版本。
许多人说这是不可能的,我会更进一步,说这不是有意义的。
一个静态成员是不涉及任何实例,只有类。
虚拟成员是不直接与任何类相关的东西,而仅仅是一个实例。
所以一个静态虚拟成员将是不涉及任何实例或任何类的东西。
前几天我遇到了这个问题:我有一些静态方法的类,但我想使用inheritance和虚拟方法,并减less代码重复。 我的解决scheme是:
而不是使用静态方法,使用虚拟方法的单身。
换句话说,每个类都应该包含一个静态方法,您可以调用该方法来获取指向该类的单个共享实例的指针。 你可以使真正的构造函数是私有的或保护的,这样外部代码就不会通过创build额外的实例来误用它。
在实践中,使用单例很像使用静态方法,只不过可以利用inheritance和虚拟方法。
有可能的!
但是,究竟是可能的,让我们缩小范围。 人们通常需要某种“静态虚函数”,因为可以通过静态调用“SomeDerivedClass :: myfunction()”和多态调用“base_class_pointer-> myfunction()”调用相同的函数所需的代码的重复。 允许这种function的“合法”方法是函数定义的重复:
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(); } };
如果基类有大量的静态函数,而派生类必须重写每一个,并且忘了为虚拟函数提供一个重复的定义。 对,我们会在运行时遇到一些奇怪的错误,很难追踪到。 导致代码重复是一件坏事。 以下试图解决这个问题(我想事先告诉它,它是完全types安全的,不包含像typeid或dynamic_cast的任何黑魔法:)
所以,我们只想为每个派生类提供getTypeInformation()的一个定义,并且显然它必须是静态函数的定义,因为如果getTypeInformation()是不可能调用“SomeDerivedClass :: getTypeInformation()”虚拟。 我们如何通过指向基类的方法来调用派生类的静态函数? 这是不可能的,因为vtable存储指针只有虚拟function,因为我们决定不使用虚拟function,我们不能修改我们的利益的vtable。 然后,为了能够通过指向基类的指针访问派生类的静态函数,我们必须以某种方式在其基类中存储对象的types。 一种方法是使用“奇怪的循环模板模式”来进行基类模板化,但这里并不合适,我们将使用一种称为“types擦除”的技术:
class TypeKeeper { public: virtual string getTypeInformation() = 0; }; template<class T> class TypeKeeperImpl: public TypeKeeper { public: virtual string getTypeInformation() { return T::getTypeInformationStatic(); } };
现在我们可以用一个variables“keeper”在基类“Object”中存储一个对象的types:
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()
优点:
- 减less代码的重复(但是我们必须在每个构造函数中调用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?
有可能的。 使两个function:静态和虚拟
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 };
不,这是不可能的,因为静态成员函数缺lessthis
指针。 而静态成员(函数和variables)实际上并不是真正的类成员。 它们恰好被ClassName::member
调用,并且遵守类访问说明符。 他们的存储被定义在课外的某个地方。 每次实例化类的对象时,都不会创build存储。 指向类成员的指针在语义和语法上是特殊的。 指向静态成员的指针在所有方面都是正常的指针。
一个类中的虚函数需要this
指针,并且与类非常耦合,因此它们不能是静态的。
那么,相当晚的答案,但它可能使用奇怪的循环模板模式。 这个维基百科文章有你需要的信息,也是静态多态性的例子是你所要求的。
虽然Alsk已经给出了一个非常详细的答案,我想补充一个替代scheme,因为我认为他的增强实现过于复杂。
我们从抽象基类开始,为所有对象types提供接口:
class Object { public: virtual char* GetClassName() = 0; };
现在我们需要一个实际的实现。 但是为了避免同时写入静态方法和虚拟方法,我们将会使我们的实际对象类inheritance虚拟方法。 如果基类知道如何访问静态成员函数,这显然是行得通的。 所以我们需要使用一个模板,并将实际的对象类名称传递给它:
template<class ObjectType> class ObjectImpl : public Object { public: virtual char* GetClassName() { return ObjectType::GetClassNameStatic(); } };
最后,我们需要实现我们的真实对象。 这里我们只需要实现静态成员函数,虚拟成员函数将从ObjectImpl模板类inheritance,用派生类的名称实例化,所以它将访问它的静态成员。
class MyObject : public ObjectImpl<MyObject> { public: static char* GetClassNameStatic() { return "MyObject"; } }; class YourObject : public ObjectImpl<YourObject> { public: static char* GetClassNameStatic() { return "YourObject"; } };
我们来添加一些代码来testing:
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 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是非静态成员的一个class.due,静态成员函数不能访问vptr,所以静态成员可以不是虚拟的。
不,它不可能,因为静态成员在编译时绑定,而虚拟成员在运行时绑定。
首先,OP所要求的回答是正确的:虚拟方法取决于实例的运行时types; 静态函数明确地不依赖于一个实例 – 只是一个types。 也就是说,让静态函数返回一个特定的types是有意义的。 例如,我有一个MouseTool类的状态模式的家庭,我开始让每个人都有一个静态函数返回键盘修改器与它一起去; 我在制作正确的MouseTool实例的工厂函数中使用了这些静态函数。 该函数检查MouseToolA :: keyboardModifier(),MouseToolB :: keyboardModifier()等鼠标状态,然后实例化适当的。 当然,后来我想检查状态是否正确,所以我想写一些类似“if(keyboardModifier == dynamic_type(* state):: keyboardModifier())”(不是真正的C ++语法),这就是这个问题。
所以,如果你发现自己想要这个,你可能想要解决你的问题。 不过,我明白有一个静态方法的愿望,然后根据dynamictypes的实例dynamic调用它们。 我认为访客模式可以给你想要的东西。 它给你你想要的。 这是一些额外的代码,但它可能对其他访客有用。
请参阅: 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. };
然后select合适的静态function的具体访客:
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(); } };
也许你可以尝试我的解决scheme如下:
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) { //remember that: plz never delete mSelf or reset the Value of mSelf in any deconstructors } class DerivedClass : public Base { public: DerivedClass(void) : Base() {} ~DerivedClass(void){} pubic: 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; }
像其他人所说,有两个重要的信息:
- 进行静态函数调用时没有
this
指针 -
this
指针指向虚拟表或thunk用于查找要调用哪个运行时方法的结构。
静态函数是在编译时确定的。
我在类中的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; }