c – 通过抽象模板基类接口指针访问派生类方法,在接口中没有显式
这是我的第一篇文章.我花了几个小时检查我的问题的解决方案,搜索链接后SO上的链接,但没有一个完全描述我的问题(我可以得到的最接近的是
this和
this).那么,让我们开始工作吧!
描述:我必须实现一组专门的类,每个类都能够存储其类型的链表.另外(棘手的部分),我必须实现一个集合管理器,以便为集合添加更多专用类不会影响其代码. 让我解释一下到目前为止. class IList { public: virtual IList& operator+( IList&) = 0; virtual void print() = 0; virtual int g_Size() const = 0; //perfect till here virtual void Push(const int&) = 0;//needs to be upgraded virtual const int& operator[](int index) = 0;//needs to be upgraded }; template<class T> class Queue: public IList{ //internal stuff public: Queue(); int g_Size() const; void print(); void Push(const T& cv); const T& operator[](int index); ~Queue(); };//all implementation of Queue<T> is implemented and working,but removed for simplicity class CIntList : public Queue<int>{ //stuff here,specialized on int }; class C_Manager{ IList * classes[3];//notice the polymorphism,managing the class collection using a pointer to the common(base) interface public: void testing() { for (int i = 0; i < 3; i++) classes[i] = new CIntList(i); classes[0]->Push(1); classes[0]->Push(2); classes[1]->Push(1121); classes[2]->Push(12); classes[0]->print(); classes[2]->print(); int a = classes[0]->operator[](1); classes[1]->Push(a + a); } //working fine }; 好的,所以你可能会问,问题是什么? 我不想重新声明Push和operator [](或任何其他使用模板作为参数的函数)来实现我的所有类特化.更确切地说,如果我想添加,让我们说, class CFloatList: public Queue<float> { //float stuff goes here }; 我还必须修改IList class IList { public: virtual IList& operator+( IList&) = 0; virtual void print() = 0; virtual int g_Size() const = 0; //perfect till here virtual void Push(const int&) = 0;//used for int virtual const int& operator[](int index) = 0;//used for int //NEW DECLARATION FOR FLOAT virtual void Push(const float&) = 0;//used for float virtual const float& operator[](int index) = 0;//used for float }; 我怎样才能避免这些重新声明?我需要一些“虚函数模板”,但C不支持. 我的方法有误吗? 很抱歉没有突出显示c语法,这是我的第一篇文章,我只是设法在代码块中格式化它.感谢您的时间! 编辑#1 我修改了IList class IList { public: virtual IList& operator+( IList&) = 0; virtual void afis() = 0; virtual int g_Size() const = 0; //templates template<typename T> void Push(const T& arg) //WORKS PERFECTLY { Queue<T>* cast = dynamic_cast<Queue<T>*>(this); cast->Push(arg); } template<typename T> const T& operator[](int index) //error { Queue<T>* cast = dynamic_cast<Queue<T>*>(this); return cast->operator[](index); } }; 和void C_Manager :: testing()到 class C_Manager{ public: void testing() { IList * a = new CIntList(1); a->Push(200);//WORKS PERFECTLY int c = a->operator[](0); //ERROR } }; 它会产生这些错误 Error C2783 'const T &IList::operator [](int)': could not deduce template argument for 'T' Error C2672 'IList::operator []': no matching overloaded function found intellisense: no instance of function template "IList::operator[]" matches the argument list 基本上,它抱怨每个可能模板化的函数都有一个与T相关的返回类型.我怎样才能解决这个问题,让我的经理真正变成多态? 解决方法
首先,让我们回顾一下您的要求:
>有一个非模板化的多态基类,IList 最后一点是关键:您正在尝试根据调用者的运行时类型和参数的静态类型来选择函数的行为.但是,哪种类型实际上与实现Queue< T>中的T匹配.在运行时确定. 基于两个对象的运行时类型的行为的运行时确定(因为参数在运行时以及编译时已知)是多方法的用途. C没有本机多方法支持,但可以与dynamic_cast拼接在一起 我通过this answer了解了与当前问题的相似之处,它提供了一系列精彩的链接,以获取有关在C中实现(和实现)完整多方法功能的更多细节. 现在,在C中使用多方法的暴力/天真实现需要从实现类型列表中测试每个可能的实现类型的参数.这是你也表示你不想要的东西,但不要担心:你不需要.这是因为我们只想测试一种情况,而不是典型的多方法情况所需的许多情况.我们将在编译时添加参数的类型,以便我们可以方便地使用该信息来查找我们感兴趣的唯一目标类型的类型. 对于提供的T类型,我们想要测试我们要调度的类型是否真的是Queue< T>. 为此,我们将使用更简单的多方法实现中使用的相同测试:dynamic_cast.具体来说,我们将把this指针强制转换为我们正在测试的类型,使用提供的参数类型作为所需模板参数的源. 警告:这意味着如果没有显式模板参数,类型之间的隐式转换将不会发生.如果将字符串文字传递给std :: string容器并且没有明确指定你想要一个std :: string容器,那么它将查找一个容器,该容器包含字符串文字长度的字符数组,并检测没有.毕竟,他们是不同的类型. 话虽如此,让我们来看看代码.对于由各种Child< T>实现的接口Parent,您可以使用它来从Child< T>获得T特定行为.只能通过Parent接口访问: class Parent{ public: template <typename T> void foo(const T& t); virtual ~Parent(){} }; template <typename T> class Child : public Parent{ public: void foo(const T& t); }; // must be after the definition of the Child template,// because dynamic_cast requires a complete type to target template <typename T> void Parent::foo(const T& t){ // throws on bad conversion like we want auto castThis = dynamic_cast<Child<T>&>(*this); // if execution reaches this point,this is a Child<T> castThis.foo(t); } 附: template<typename T> void Child<T>::foo(const T& t){ std::cout << typeid(T).name() << ": " << t << 'n'; } int main(){ Parent&& handle = Child<int>(); try{ handle.foo<int>(3); handle.foo<char>(0); handle.foo<std::string>("Hello!"); } catch(std::bad_cast e){ std::cout << "bad cast caughtn"; } } 我们得到以下输出on both g++ 5.2.0 and clang 3.7 i: 3 bad cast caught 这就是我们想要的. 一旦你在这里提供了简单的多态接口,实现你的集合应该很容易.我将使用围绕std :: vector< std :: unique_ptr< Parent>>的包装类.我自己,但这个决定最终取决于你. 现在,因为这还不够文本墙,有些注意事项: >抛出异常对标准控制流程不利.如果您实际上并不知道参数是否通过某些外部逻辑与基础类型匹配,那么您需要一些其他形式的错误处理. dynamic_cast可用于转换引用和指针.对不属于目标类型的对象进行引用会抛出std :: bad_cast.转换指针将返回空指针.
因此,对于foo的查找将在Child< T>中开始,并且因为它在Child< T>内找到具有该名称的成员函数,所以它不检查Parent或再次调用调度函数.?3.在实际使用这种解决方法之前,我会考虑为什么我要这么做. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |