C++ 我是否可以在不使用虚拟函数的情况下获得多态行为?
因为我的设备,我不能使用虚拟功能。假设我有:C++ 我是否可以在不使用虚拟函数的情况下获得多态行为?,c++,function,polymorphism,virtual,overriding,C++,Function,Polymorphism,Virtual,Overriding,因为我的设备,我不能使用虚拟功能。假设我有: class Base { void doSomething() { } }; class Derived : public Base { void doSomething() { } }; // in any place { Base *obj = new Derived; obj->doSomething(); } obj->doSomething()将只调用Base::doSomething() 使用B
class Base
{
void doSomething() { }
};
class Derived : public Base
{
void doSomething() { }
};
// in any place
{
Base *obj = new Derived;
obj->doSomething();
}
obj->doSomething()
将只调用Base::doSomething()
使用Base*obj
是否有方法调用派生的的doSomething
我知道我可以在Base的doSomething()
之前放置一个virtual
,它可以解决问题,但我受设备的限制,编译器不支持它。您可以向下转换基类指针到派生类并调用函数
Base* obj = new Derived;
Derived* d = static_cast<Derived*>( obj );
d->doSomething();
#include<iostream>
using namespace std;
class Base
{
public:
void display()
{
cout<<"From Base class\n";
}
};
class Derived:public Base
{
public:
void display()
{
cout<<"From Derived class";
}
};
int main()
{
Base *ptr=new Derived;
Derived* d = static_cast<Derived*>(ptr);
ptr->display();
d->display();
return 0;
}
Base*obj=新派生;
导出*d=静态_转换(obj);
d->doSomething();
由于doSomething()
未声明为virtual
,因此应该获得派生实现 如果没有虚拟方法,就没有简单的方法可以做到这一点。你能封装基类而不是从基类派生吗
然后可以调用doSomething()//获取派生值
或者base->doSomething()//调用base您可以将对象向下转换为派生类型并调用它,如下所示:
static_cast<Derived*>(obj)->doSomething();
static_cast(obj)->doSomething();
尽管这并不能保证“obj”所指的是真正的派生类型
我更担心的是你甚至连虚拟功能都没有。如果您的函数都不是虚拟的,并且您正在子类化,那么析构函数是如何工作的?我想您可以自己创建vtable。我只是一个包含“虚拟”函数指针的结构,作为Base的一部分,并且有代码来设置vtable
这是一个粗略的解决方案——C++编译器的任务来处理这个特性。
但这里有:
#include <iostream>
class Base
{
protected:
struct vt {
void (*vDoSomething)(void);
} vt;
private:
void doSomethingImpl(void) { std::cout << "Base doSomething" << std::endl; }
public:
void doSomething(void) { (vt.vDoSomething)();}
Base() : vt() { vt.vDoSomething = (void(*)(void)) &Base::doSomethingImpl;}
};
class Derived : public Base
{
public:
void doSomething(void) { std::cout << "Derived doSomething" << std::endl; }
Derived() : Base() { vt.vDoSomething = (void(*)(void)) &Derived::doSomething;}
};
#包括
阶级基础
{
受保护的:
结构{
无效(*VDO方法)(无效);
}vt;
私人:
void doSomethingImpl(void){std::cout当然可以做到这一点;这并不一定容易
如果有一个派生类的有限列表,当你定义基类时,你知道它们是什么,你可以使用一个非多态成员函数包装器来做这个。这里有一个带有两个派生类的例子。它不使用标准库工具,只依赖标准的C++特性。
class Base;
class Derived1;
class Derived2;
class MemFnWrapper
{
public:
enum DerivedType { BaseType, Derived1Type, Derived2Type };
typedef void(Base::*BaseFnType)();
typedef void(Derived1::*Derived1FnType)();
typedef void(Derived2::*Derived2FnType)();
MemFnWrapper(BaseFnType fn) : type_(BaseType) { fn_.baseFn_ = fn; }
MemFnWrapper(Derived1FnType fn) : type_(Derived1Type) {fn_.derived1Fn_ = fn;}
MemFnWrapper(Derived2FnType fn) : type_(Derived2Type) {fn_.derived2Fn_ = fn;}
void operator()(Base* ptr) const;
private:
union FnUnion
{
BaseFnType baseFn_;
Derived1FnType derived1Fn_;
Derived2FnType derived2Fn_;
};
DerivedType type_;
FnUnion fn_;
};
class Base
{
public:
Base() : doSomethingImpl(&Base::myDoSomething) { }
Base(MemFnWrapper::Derived1FnType f) : doSomethingImpl(f) { }
Base(MemFnWrapper::Derived2FnType f) : doSomethingImpl(f) { }
void doSomething() { doSomethingImpl(this); }
private:
void myDoSomething() { }
MemFnWrapper doSomethingImpl;
};
class Derived1 : public Base
{
public:
Derived1() : Base(&Derived1::myDoSomething) { }
private:
void myDoSomething() { }
};
class Derived2 : public Base
{
public:
Derived2() : Base(&Derived2::myDoSomething) { }
private:
void myDoSomething() { }
};
// Complete the MemFnWrapper function call operator; this has to be after the
// definitions of Derived1 and Derived2 so the cast is valid:
void MemFnWrapper::operator()(Base* ptr) const
{
switch (type_)
{
case BaseType: return (ptr->*(fn_.baseFn_))();
case Derived1Type: return (static_cast<Derived1*>(ptr)->*(fn_.derived1Fn_))();
case Derived2Type: return (static_cast<Derived2*>(ptr)->*(fn_.derived2Fn_))();
}
}
int main()
{
Base* obj0 = new Base;
Base* obj1 = new Derived1;
Base* obj2 = new Derived2;
obj0->doSomething(); // calls Base::myDoSomething()
obj1->doSomething(); // calls Derived1::myDoSomething()
obj2->doSomething(); // calls Derived2::myDoSomething()
}
类基;
派生类1;
派生类2;
类MemFnWrapper
{
公众:
枚举DerivedType{BaseType,Derived1Type,Derived2Type};
typedef void(Base::*BaseFnType)();
typedef void(Derived1::*derived1ntype)();
typedef void(Derived2::*derived2ntype)();
MemFnWrapper(BaseFnType-fn):类型(BaseType){fn_0.baseFn_0=fn;}
MemFnWrapper(Derived1Ntype fn):类型(Derived1Type){fn_0.derived1Fn_0=fn;}
MemFnWrapper(Derived2Ntype fn):类型(Derived2Type){fn_0.derived2Fn_0=fn;}
void运算符()(基*ptr)常量;
私人:
工会
{
baseFn类型baseFn;
Derived1Ntype derived1Fn;
Derived2Ntype derived2Fn_2;;
};
派生类型;
fn联盟fn_u2;;
};
阶级基础
{
公众:
Base():doSomethingImpl(&Base::myDoSomething){}
Base(MemFnWrapper::Derived1FnType f):doSomethingImpl(f){}
Base(MemFnWrapper::Derived2FnType f):doSomethingImpl(f){}
void doSomething(){doSomethingImpl(this);}
私人:
void myDoSomething(){}
MemFnWrapper doSomethingImpl;
};
类Derived1:公共基
{
公众:
Derived1():Base(&Derived1::myDoSomething){}
私人:
void myDoSomething(){}
};
派生类2:公共基
{
公众:
Derived2():Base(&Derived2::myDoSomething){}
私人:
void myDoSomething(){}
};
//完成MemFnWrapper函数调用操作符;这必须在
//Derived1和Derived2的定义,以便强制转换有效:
void MemFnWrapper::operator()(Base*ptr)const
{
开关(类型)
{
case-BaseType:return(ptr->*(fn_.baseFn_))();
case-derived1类型:return(static_cast(ptr)->*(fn_.derived1Fn_));
case-Derived2Type:return(static_cast(ptr)->*(fn_.derived2Fn_));
}
}
int main()
{
Base*obj0=新基础;
Base*obj1=新衍生的1;
Base*obj2=新衍生的2;
obj0->doSomething();//调用Base::myDoSomething()
obj1->doSomething();//调用Derived1::myDoSomething()
obj2->doSomething();//调用Derived2::myDoSomething()
}
(我最初建议使用std::function
,这为您做了大量的工作,但后来我记得它是一个多态函数包装器,因此它必须使用虚拟函数。:-p Oops。您可以查看修订历史记录,以查看它的外观)由于虚拟方法通常是通过vtables实现的,所以没有什么神奇的事情是你不能在代码中复制的。事实上,你可以实现自己的虚拟分派机制。这需要一些工作,实现基类的程序员和实现派生类的程序员都要做,但是它工作
抛出指针,就像CeeLulLIS所建议的那样,可能是你应该考虑的第一件事。但是我在这里发布的解决方案至少给了你编写使用这些类的代码的机会,就好像你的编译器支持<代码>虚拟< /代码>。也就是说,用一个简单的函数调用。
这是一个实现Base
类的程序,该类具有返回字符串的函数
:“Base”,以及返回字符串
:“der”的派生类。其思想是能够支持这样的代码:
Base* obj = new Der;
cout << obj->get_string();
这表明,在不依赖语言对多态性的支持的情况下,确实有可能获得至少一种有限形式的多态性行为
然而,这个例子有大量的样板文件,它肯定不能很好地扩展:对于您添加的每个类,您必须修改代码中的六个不同位置,对于您想要支持的每个成员函数,您需要复制大部分代码
#include <boost/preprocessor.hpp>
// [INTERNAL] PSEUDOPM_INIT_VTABLE Support
#define PSEUDOPMX_INIT_VTABLE_ENTRY(r, c, i, fn) \
BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(0, i)) \
& c :: BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(4, 0, fn), Impl)
// [INTERNAL] PSEUDOPM_DECLARE_VTABLE Support
#define PSEUDOPMX_DECLARE_VTABLE_STRUCT_MEMBER(r, c, i, fn) \
BOOST_PP_TUPLE_ELEM(4, 1, fn) \
(c :: * BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(4, 0, fn), Ptr)) \
BOOST_PP_TUPLE_ELEM(4, 3, fn);
#define PSEUDOPMX_DECLARE_VTABLE_STRUCT(r, memfns, c) \
struct BOOST_PP_CAT(PseudoPMIntVTable, c) \
{ \
friend class c; \
BOOST_PP_SEQ_FOR_EACH_I(PSEUDOPMX_DECLARE_VTABLE_STRUCT_MEMBER, c, memfns)\
};
#define PSEUDOPMX_DECLARE_VTABLE_ENUM_MEMBER(r, x, i, c) \
BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(0, i)) BOOST_PP_CAT(PseudoPMType, c)
#define PSEUDOPMX_DECLARE_VTABLE_UNION_MEMBER(r, x, c) \
BOOST_PP_CAT(PseudoPMIntVTable, c) BOOST_PP_CAT(BOOST_PP_CAT(table_, c), _);
#define PSEUDOPMX_DECLARE_VTABLE_RESET_FN(r, x, c) \
void Reset(BOOST_PP_CAT(PseudoPMIntVTable, c) table) \
{ \
type_ = BOOST_PP_CAT(PseudoPMType, c); \
table_.BOOST_PP_CAT(BOOST_PP_CAT(table_, c), _) = table; \
}
#define PSEUDOPMX_DECLARE_VTABLE_PUBLIC_FN(r, x, fn) \
BOOST_PP_TUPLE_ELEM(4, 1, fn) \
BOOST_PP_TUPLE_ELEM(4, 0, fn) \
BOOST_PP_TUPLE_ELEM(4, 3, fn);
// [INTERNAL] PSEUDOPM_DEFINE_VTABLE Support
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST0
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST1 a0
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST2 a0, a1
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST3 a0, a1, a2
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST4 a0, a1, a2, a3
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST5 a0, a1, a2, a3, a4
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST6 a0, a1, a2, a3, a4, a5
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST7 a0, a1, a2, a3, a4, a5, a6
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST8 a0, a1, a2, a3, a4, a5, a6, a7
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST9 a0, a1, a2, a3, a4, a5, a6, a7, a8
#define PSEUDOPMX_DEFINE_VTABLE_FNP(r, x, i, t) \
BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(0, i)) \
t BOOST_PP_CAT(a, i)
#define PSEUDOPMX_DEFINE_VTABLE_FN_CASE(r, fn, i, c) \
case BOOST_PP_CAT(PseudoPMType, c) : return \
( \
static_cast<c*>(this)->*pseudopm_vtable_.table_. \
BOOST_PP_CAT(BOOST_PP_CAT(table_, c), _). \
BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(4, 0, fn), Ptr) \
)( \
BOOST_PP_CAT( \
PSEUDOPMX_DEFINE_VTABLE_ARGLIST, \
BOOST_PP_TUPLE_ELEM(4, 2, fn) \
) \
);
#define PSEUDOPMX_DEFINE_VTABLE_FN(r, classes, fn) \
BOOST_PP_TUPLE_ELEM(4, 1, fn) \
BOOST_PP_SEQ_HEAD(classes) :: BOOST_PP_TUPLE_ELEM(4, 0, fn) \
( \
BOOST_PP_SEQ_FOR_EACH_I( \
PSEUDOPMX_DEFINE_VTABLE_FNP, x, \
BOOST_PP_TUPLE_TO_SEQ( \
BOOST_PP_TUPLE_ELEM(4, 2, fn), \
BOOST_PP_TUPLE_ELEM(4, 3, fn) \
) \
) \
) \
{ \
switch (pseudopm_vtable_.type_) \
{ \
BOOST_PP_SEQ_FOR_EACH_I(PSEUDOPMX_DEFINE_VTABLE_FN_CASE, fn, classes) \
} \
}
// Each class in the classes sequence should call this macro at the very
// beginning of its constructor. 'c' is the name of the class for which
// to initialize the vtable, and 'memfns' is the member function sequence.
#define PSEUDOPM_INIT_VTABLE(c, memfns) \
BOOST_PP_CAT(PseudoPMIntVTable, c) pseudopm_table = \
{ \
BOOST_PP_SEQ_FOR_EACH_I(PSEUDOPMX_INIT_VTABLE_ENTRY, c, memfns) \
}; \
pseudopm_vtable_.Reset(pseudopm_table);
// The base class should call this macro in its definition (at class scope).
// This defines the virtual table structs, enumerations, internal functions,
// and declares the public member functions. 'classes' is the sequence of
// classes and 'memfns' is the member function sequence.
#define PSEUDOPM_DECLARE_VTABLE(classes, memfns) \
protected: \
BOOST_PP_SEQ_FOR_EACH(PSEUDOPMX_DECLARE_VTABLE_STRUCT, memfns, classes) \
\
enum PseudoPMTypeEnum \
{ \
BOOST_PP_SEQ_FOR_EACH_I(PSEUDOPMX_DECLARE_VTABLE_ENUM_MEMBER, x, classes) \
}; \
\
union PseudoPMVTableUnion \
{ \
BOOST_PP_SEQ_FOR_EACH(PSEUDOPMX_DECLARE_VTABLE_UNION_MEMBER, x, classes) \
}; \
\
class PseudoPMVTable \
{ \
public: \
BOOST_PP_SEQ_FOR_EACH(PSEUDOPMX_DECLARE_VTABLE_RESET_FN, x, classes) \
private: \
friend class BOOST_PP_SEQ_HEAD(classes); \
PseudoPMTypeEnum type_; \
PseudoPMVTableUnion table_; \
}; \
\
PseudoPMVTable pseudopm_vtable_; \
\
public: \
BOOST_PP_SEQ_FOR_EACH(PSEUDOPMX_DECLARE_VTABLE_PUBLIC_FN, x, memfns)
// This macro must be called in some source file after all of the classes in
// the classes sequence have been defined (so, for example, you can create a
// .cpp file, include all the class headers, and then call this macro. It
// actually defines the public member functions for the base class. Each of
// the public member functions calls the correct member function in the
// derived class. 'classes' is the sequence of classes and 'memfns' is the
// member function sequence.
#define PSEUDOPM_DEFINE_VTABLE(classes, memfns) \
BOOST_PP_SEQ_FOR_EACH(PSEUDOPMX_DEFINE_VTABLE_FN, classes, memfns)
// The sequence of classes in the class hierarchy. The base class must be the
// first class in the sequence. Derived classes can be in any order.
#define CLASSES (Base)(Derived)
// The sequence of "virtual" member functions. Each entry in the sequence is a
// four-element tuple:
// (1) The name of the function. A function will be declared in the Base class
// with this name; it will do the dispatch. All of the classes in the class
// sequence must implement a private implementation function with the same
// name, but with "Impl" appended to it (so, if you declare a function here
// named "Foo" then each class must define a "FooImpl" function.
// (2) The return type of the function.
// (3) The number of arguments the function takes (arity).
// (4) The arguments tuple. Its arity must match the number specified in (3).
#define VIRTUAL_FUNCTIONS \
((FuncNoArg, void, 0, ())) \
((FuncOneArg, int, 1, (int))) \
((FuncTwoArg, int, 2, (int, int)))
class Base
{
public:
Base()
{
PSEUDOPM_INIT_VTABLE(Base, VIRTUAL_FUNCTIONS)
}
PSEUDOPM_DECLARE_VTABLE(CLASSES, VIRTUAL_FUNCTIONS)
private:
void FuncNoArgImpl() { }
int FuncOneArgImpl(int x) { return x; }
int FuncTwoArgImpl(int x, int y) { return x + y; }
};
class Derived : public Base
{
public:
Derived()
{
PSEUDOPM_INIT_VTABLE(Derived, VIRTUAL_FUNCTIONS)
}
private:
void FuncNoArgImpl() { }
int FuncOneArgImpl(int x) { return 2 * x; }
int FuncTwoArgImpl(int x, int y) { return 2 * (x + y); }
};
PSEUDOPM_DEFINE_VTABLE(CLASSES, VIRTUAL_FUNCTIONS)
#include <cassert>
int main()
{
Base* obj0 = new Base;
Base* obj1 = new Derived;
obj0->FuncNoArg(); // calls Base::FuncNoArg
obj1->FuncNoArg(); // calls Derived::FuncNoArg
assert(obj0->FuncTwoArg(2, 10) == 12); // Calls Base::FuncTwoArg
assert(obj1->FuncTwoArg(2, 10) == 24); // Calls Derived::FuncTwoArg
}
template<class SomethingDoer> class MyClass
{
public:
void doSomething() {myDoer.doSomething();}
private:
SomethingDoer myDoer;
};
class BaseSomethingDoer
{
public:
void doSomething() { // base implementation }
};
class DerivedSomethingDoer
{
public:
void doSomething() { // derived implementation }
};
typedef MyClass<BaseSomethingDoer> Base;
typedef MyClass<DerivedSomethingDoer> Derived;
#include <iostream>
template<class T> struct base{
void g(){
if(T *p = static_cast<T *>(this)){
p->f();
}
}
void f(){volatile int v = 0; std::cout << 1;}
virtual ~base(){}
};
struct derived1 : base<derived1>{
void f(){std::cout << 2;}
};
struct derived2 : base<derived2>{
void f(){std::cout << 3;}
};
int main(){
derived1 d1;
d1.g();
derived2 d2;
d2.g();
}
#include<iostream>
using namespace std;
class Base
{
public:
void display()
{
cout<<"From Base class\n";
}
};
class Derived:public Base
{
public:
void display()
{
cout<<"From Derived class";
}
};
int main()
{
Base *ptr=new Derived;
Derived* d = static_cast<Derived*>(ptr);
ptr->display();
d->display();
return 0;
}