导出类指针可以被当成基类指针使用吗?
书上有云:“在通过指针或引用方式操作时,派生类对象可以当作基类对象看待和处理......用派生类指针给基类指针赋值时,不需要显示转换”。可是,如下代码:
class Base{
public:
void doIt(const char *){cout <<"Base do it";}
};
class Derived: public Base{
public:
void doIt(int){cout << "Deriver do it";}
};
template<class T,class X>
void TestDoit (X x,T p){
p->doIt(x);
}
int main(){
Derived * p = new Derived();
TestDoit ("abcdefg", p);
}
却不能在VS2008上通过编译。提示如下:
error C2664: “Derived::doIt”: 不能将参数 1 从“const char *”转换为“int”
没有使该转换得以执行的上下文
.\重载.cpp(20): 参见对正在编译的函数 模板 实例化“void TestDoit<Derived*,const char*>(X,T)”的引用
with
[
X=const char *,
T=Derived *
]
我不明白,为何不能通过指针“等价”去匹配“void TestDoit<Base*,int>(X,T)”,却一定要吊死在“void TestDoit<Derived*,const char*>(X,T)”这棵树上?!
请高手赐教!谢谢
[解决办法]
Base * p = new Derived();
[解决办法]
函数模板在编译时做匹配,你传入的参数是什么类型就生成什么代码。
除非你显示的告诉编译期你传入的类型
TestDoit <Base*, const char*>("abcdefg", p); 这样p就会转换成基类指针
[解决办法]
楼主,多了解一下 类成员函数的重载、覆盖、隐藏这方面的东东
[解决办法]
class Derived;class Base{ friend void TestDoit (Derived*); // 友元 void doIt(const char*){cout <<"Base do it";}};class Derived: public Base{};void TestDoit (Derived*p){ p->doIt("hello");}int main(){ Derived * p = new Derived(); TestDoit (p); system("pause"); return 0;}
------解决方案--------------------
你TestDoit声明为友元当然可以访问了。
[解决办法]
template <typename RT, typename T1, typename T2>
inline RT max (T1 const& a, T2 const& b);
...
max<double>(4,4.2);// OK,返回类型为 double
此例之中,我们调用 max()时,只明确指出回传类型 RT 为 double,至于 T1 和 T2 两个参数型别会被编译程序根据调用时的自变量推导为 int 和 double。
注意,这些max()修改版本并没带来什么明显好处。在「单一参数」版本中,如果两个自变量的类型不同,你可以指定参数类型和回返值类型。总之,为尽量保持程序代码简单,使用「单一参数」的 max()是不错的主意。讨论其他 template 相关问题时,我们也会遵守这个原则。
自变量推导过程的细节将在第 11 章讨论。
6推导过程也可以看作是重载决议机制(overload resolution)的一部份,两者都不倚赖回传值的类型来区分不同的调用。惟一的例外是:转型运算符成员函数(conversion operator members)倚赖回传型别来进行重载决议(overload resolution)。(译注:「转型运算符」函数名称形式如下:operator type(),其中的 type 可为任意类型;无需另外指出回传类型,因为函数名称已经表现出回传类型。)
2.4.重载(Overloading)Function Templates
就像常规(意即non-template)functions 一样,function templates 也可以被重载(译注:C++标准链接库中的许多 STL 算法都是如此)。这就是说,你可以写出多个不同的函数定义,并使用相同的函数名称;当客户调用其中某个函数时,C++编译程序必须判断应该唤起哪一个函数。即使不牵扯 templates,这个推断过程也非常复杂。本节讨论的是,一旦涉及 templates,重载将是一个怎样的过程。如果你对 non-templates 情况下的重载机制还不太清楚,可以先参考附录 B,那里我们对重载机制做了相当深入的讲解。
下面这个小程序展示如何重载一个 function template:
// basics/max2.cpp
// 传回两个 ints 中的较大者
inline int const& max (int const& a, int const& b)
{
return a < b ? b : a;
}
// 传回两任意类型的数值中的较大者
template <typename T>
inline T const& max (T const& a, T const& b)
{
return a < b ? b : a;
}
// 传回三个任意类型值中的最大者
template <typename T>
inline T const& max (T const& a, T const& b, T const& c)
{
return ::max (::max(a,b), c);
}
int main()
{
::max(7, 42, 68);// 唤起「接受三个自变量」的函数
::max(7.0, 42.0);// 唤起 max<double>(经由自变量推导)
::max('a', 'b');// 唤起 max<char>(经由自变量推导)
::max(7, 42);// 唤起「接受两个 int 自变量」的 non-template 函数
::max<>(7, 42);// 唤起 max<int>(经由自变量推导)
::max<double>(7, 42); // 唤起 max<double>(无需自变量推导)
::max('a', 42.7);// 唤起「接受两个 int 自变量」的 non-template 函数
}
/* 译注:ICL7.1/g++ 3.2 顺利通过本例。VC6 无法把最后一个调用匹配到常规的(non-template)函数 max(),造成编译失败。VC7.1 可顺利编译,但对倒数第二个调用给出警告:虽然它唤起的是 function template max(),但它发现常规函数 max()与这个调用更匹配。*/
这个例子说明:non-template function 可以和同名的 function template 共存,也可以和其相同类型的具现体共存。当其他要素都相等时,重载决议机制会优先选择 non-template function,而不选择由 function template 具现化后的函数实体。上述第四个调用便是遵守这条规则:
::max(7, 42); // 两个自变量都是 int,吻合对应的 non-template function
但是如果可由 template 产生更佳匹配,则 template 具现体会被编译程序选中。前述的第二和第三个调用说明了这一点:
::max(7.0, 42.0);// 唤起 max<double>(经由自变量推导)
::max('a', 'b');// 唤起 max<char>(经由自变量推导)
调用端也可以使用空的 template argument list,这种形式告诉编译程序「只从 template 具现体中挑选适当的调用对象」,所有 template parameters 都自 call parameters 推导而得:
::max<>(7, 42);// 唤起 max<int>(经由自变量推导)
另外,「自动类型转换」只适用于常规函数,在 templates 中不予考虑,因此前述最后一个调用唤起的是 non-template函数。在该处,'a'和 42.7 都被转型为 int:
::max('a', 42.7);// 本例中只有 non-template 函数才可以接受两个不同类型的自变量
下面是一个更有用的例子,为指针类型和 C-style 字符串类型重载了 max()template:
// basics/max3.cpp
#include <iostream>
#include <cstring>
#include <string>
// 传回两个任意类型值的较大者
template <typename T>
inline T const& max (T const& a, T const& b)
{
return a < b ? b : a;
}
// 传回两个指针的较大者(所谓较大是指「指针所指之物」较大)
template <typename T>
inline T* const& max (T* const& a, T* const& b)
{
return *a < *b ? b : a;
}
// 传回两个 C-style 字符串的较大者(译注:C-style 字符串必须自行定义何谓「较大」)。
inline char const* const& max (char const* const& a, char const* const& b)
{
return std::strcmp(a,b) < 0 ? b : a;
}
int main()
{
int a=7;
int b=42;
::max(a,b);// 唤起「接受两个 int」的 max()
std::string s = "hey";
std::string t = "you";
::max(s,t);// 唤起「接受两个 std::string」的 max()
int *p1 = &b;
int *p2 = &a;
::max(p1,p2);// 唤起「接受两个指针」的 max()
char const* s1 = "David";
char const* s2 = "Nico";
::max(s1, s2);// 唤起「接受两个 C-style 字符串」的 max()
}
注意,所有重载函数都使用 by reference 方式来传递自变量。一般说来,不同的重载形式之间最好只存在「绝对必要的差异」。各重载形式之间应该只存在「参数个数的不同」或「参数类型的明确不同」,否则可能引发各种副作用。举个例子,如果你以一个「by value 形式的 max()」重载一个「by reference 形式的 max()」(译注:两者之间的差异不够明显),就无法使用「三自变量」版本的 max()来取得「三个 C-style 字符串中的最大者」:
// basics/max3a.cpp
#include <iostream>
#include <cstring>
#include <string>
// 传回两个任意类型值的较大者(call-by-reference)
template <typename T>
inline T const& max (T const& a, T const& b)
{
return a < b ? b : a;
}
// 传回两个 C-style 字符串的较大者(call-by-value)
inline char const* max (char const* a, char const* b)
{
return std::strcmp(a,b) < 0 ? b : a;
}
// 传回三个任意类型值的最大者(call-by-reference)
template <typename T>
inline T const& max (T const& a, T const& b, T const& c)
{
return ::max (::max(a,b), c); // 当 max(a,b)采用 by value 形式时,此行错误
}
int main()
{
::max(7, 42, 68); // OK
const char* s1 = "frederic";
const char* s2 = "anica";
const char* s3 = "lucas";
::max(s1, s2, s3); // ERROR
}
本例中针对三个 C-style 字符串调用 max(),会出现问题。以下这行述句是错误的:
return ::max (::max(a,b), c);
因为 C-style 字符串的 max(a,b) 重载函数创建了一个新而暂时的区域值(a new, temporary local value),而该值却以 by reference 方式被传回(那当然会造成错误)。
这只是细微的重载规则所引发的非预期行为例子之一。当函数调用动作发生时,如果不是所有重载形式都在当前范围内可见,那么上述错误可能发生,也可能不发生。事实上,如果把「三自变量」版本的 max()写在接受两个 ints 的 max()前面(于是后者对前者而言不可见),那么在调用「三自变量」max()时,会间接唤起「双自变量」max() function template:
// basics/max4.cpp
// 传回两个任意类型值的较大者
template <typename T>
inline T const& max (T const& a, T const& b)
{
return a < b ? b : a;
}
// 传回三个任意类型值的最大者
template <typename T>
inline T const& max (T const& a, T const& b, T const& c)
{
return ::max (::max(a,b), c);
// 即使自变量类型都是 int,
// 这里也会调用 max()template。因为下面的函数定义来得太迟。
}
// 传回两个 ints 的较大者
inline int const& max (int const& a, int const& b)
{
return a < b ? b : a;
}
9.2 节, p.121 详细讨论这个问题。就目前而言,你应该遵循一条准则:总是把所有形式的重载函式写在它们被调用之前。
2.5.摘要
?Function templates 可以针对不同的 template arguments 定义一整族(a family of)函数。
?Function templates 将依照传递而来的自变量(arguments)的类型而被具现化(instantiated)。
?你可以明确指出 template parameters。
?Function templates 可以被重载(overloaded)。
?重载 function templates 时,不同的重载形式之间最好只存在「绝对必要的差异」。
?请确保所有形式的重载函数都被写在它们的被调用点之前。
[解决办法]
这里子类把父类的函数隐藏了,并不是多态啊,自然只能匹配子类的函数
如果new完再转成父类的指针试试,就应该可以了
Derived * p = new Derived();
Base *pBase = static_cast<Base*>(p);
TestDoit ("abcdefg", pBase);
[解决办法]
Derived * p1 = new Derived();
p1->doIt("22222");
你看一下这句能否编译通过就知道了。
[解决办法]
因为编译器知道p是Derived class的指针,它当然调用p的doit()
[解决办法]
后来,试了一下,不能在模版中使用RTTI
因为,模版只是编译时替换模版类型,不能进行选择性的编译部分代码。
即便判断出模版实例化的类型,也不能略过编译部分代码。
要是非要实现这个畸形功能,用模版的特化,或直接使用重载函数!
附上模版特化代码:
class Base{
public:
virtual void doIt(const char *){std::cout <<"Base do it";}
};
class Derived: public Base{
public:
//不是 虚函数:参数列表不同。即便加上virtual,也不能与virtual void doIt(const char *)构成统一动态绑定函数层次
void doIt(int){std::cout << "Deriver do it";}
};
template<class T,class X>
void TestDoit (X x,T p){
p->doIt(x);
}
//新增函数模版的特化
template<>
void TestDoit<Derived *,const char *>(const char * x,Derived * p){
p->Base::doIt(x);
}
//end of 新增函数模版的特化
int main(){
Derived * p = new Derived();
TestDoit ("abcdefg", p);
char ca[]="1234";
p->Base::doIt(ca);
TestDoit (10, p);
}