C++中派生类重写基类重载函数时需要注意的问题:派生类函数屏蔽基类中同名函数
2013-09-22 19:00
387 查看
派生类可以继承基类中的非私有函数成员,当然也就可以继承其中非私有的被重载的函数。如下:
【参考代码】
【运行结果】
现在,我们想要在派生类中重写其中的一个重载函数:
这样是不是就可以了呢? 我们来运行一下:
【运行结果】
结果出错了,显示说匹配不到后两种情况,这是为什么呢?
下面一段内容来自 C++ Primer:
理解 C++ 中继承层次的关键在于理解如何确定函数调用。确定函数调用遵循以下四个步骤:
1. 首先确定进行函数调用的对象、引用或指针的静态类型。
2. 在该类中查找函数,如果找不到,就在直接基类中查找,如此循着类的继承链往上找,直到找到该函数或者查找完最后一个类。如果不能在类或其相关基类中找到该名字,则调用是错误的。
3. 一旦找到了该名字,就进行常规类型检查,查看如果给定找到的定义,该函数调用是否合法。
4. 假定函数调用合法,编译器就生成代码。如果函数是虚函数且通过引用或指针调用,则编译器生成代码以确定根据对象的动态类型运行哪个函数版本,否则,编译器生成代码直接调用函数。
原来,C++中,每个类都记录着在该类中定义的函数名及类型信息,当发生函数调用时,编译器先按函数名查找,如果在该类中查不到与之匹配的函数名,则向其父类查找,依次向上递归,直至函数名匹配成功,然后进行参数类型等信息的匹配;或者查到最顶层仍未匹配到相应的函数名。
所以,当我们在派生类中没有重写重载函数之一的时候,在派生类中调用的重载函数是在其基类中查到的,因此,调用可以成功;然而,当我们仅重写了其中的一个重载函数时,在做函数名匹配时,在本类中就可以匹配到了,就不会向其父类查找了。而在派生类中,仅记录了这个被重写的函数的信息,当然也就没有另外两个重载函数的一些了,因此就导致了上述错误的出现了。 换句话说,派生类中的函数会将其父类中的同名函数屏蔽掉。
因此,如果派生类想通过自身类型使用的基类中重载版本,则派生类必须要么重定义所有重载版本,要么一个也不重定义。
那么,如果在派生类中需要且仅需要重写其中一个重载函数,必须得把其它重载函数都重定义吗?有没有简便的方法,仅重定义我们想要改变的那个,其它的还是从父类继承呢。答案是肯定的,有,而且还可以有不同的方式:
一、通过using在派生类中为父类函数成员提供声明:
前面知道,因为派生类重写的函数名屏蔽了父类中的同名函数,那么我们可以通过using来为父类函数提供声明;这样,派生类不用重定义所继承的每一个基类版本,它可以为重载成员提供 using声明。一个 using 声明只能指定一个名字,不能指定形参表,因此,为基类成员函数名称而作的 using 声明将该函数的所有重载实例加到派生类的作用域。将所有名字加入作用域之后,派生类只需要重定义本类型确实必须定义的那些函数,对其他版本可以使用继承的定义。
上面说了那么多,不知道说明白了没有,不过,看了下面的例子,你就会豁然开朗: so easy!
仅仅需要加入 using Base::print; 问题便解决了:
【运行结果】
二、通过基类指针调用
在调用被屏蔽的重载函数时,可以不直接通过派生类对象调用,而是通过基类指针指向派生类对象,通过基类指针进行调用,这样就会直接在基类中进行查找函数名,从而可以匹配并进行类型匹配。
【运行结果】
但是这样就有两种调用方式,看起来很不舒服,而且容易弄错。那么把在派生类中需要重载的那个版本相应地在基类中声明为vitual,从而可以实现动态绑定,就能统一的使用基类指针来调用了:
【参考代码】
【运行结果】
原创文章,转载请注明: 转载自
本文链接地址: http://blog.csdn.net/iicy266/article/details/11906697
【参考代码】
class Base { public: void print() { cout << "print() in Base." << endl; } void print(int a) { cout << "print(int a) in Base." << endl; } void print(string s) { cout << "print(string s) in Base." << endl; } }; class Derived : public Base { }; int main() { Derived d; d.print(); d.print(10); d.print(""); return 0; }
【运行结果】
print() in Base. print(int a) in Base. print(string s) in Base.
现在,我们想要在派生类中重写其中的一个重载函数:
class Derived : public Base { public: void print() { cout << "Rewrite print() in Derived." << endl; } };
这样是不是就可以了呢? 我们来运行一下:
【运行结果】
reload_test.cc: In function ‘int main()’: reload_test.cc:39: error: no matching function for call to ‘Derived::print(int)’ reload_test.cc:21: note: candidates are: void Derived::print() reload_test.cc:40: error: no matching function for call to ‘Derived::print(const char [1])’ reload_test.cc:21: note: candidates are: void Derived::print()
结果出错了,显示说匹配不到后两种情况,这是为什么呢?
下面一段内容来自 C++ Primer:
理解 C++ 中继承层次的关键在于理解如何确定函数调用。确定函数调用遵循以下四个步骤:
1. 首先确定进行函数调用的对象、引用或指针的静态类型。
2. 在该类中查找函数,如果找不到,就在直接基类中查找,如此循着类的继承链往上找,直到找到该函数或者查找完最后一个类。如果不能在类或其相关基类中找到该名字,则调用是错误的。
3. 一旦找到了该名字,就进行常规类型检查,查看如果给定找到的定义,该函数调用是否合法。
4. 假定函数调用合法,编译器就生成代码。如果函数是虚函数且通过引用或指针调用,则编译器生成代码以确定根据对象的动态类型运行哪个函数版本,否则,编译器生成代码直接调用函数。
原来,C++中,每个类都记录着在该类中定义的函数名及类型信息,当发生函数调用时,编译器先按函数名查找,如果在该类中查不到与之匹配的函数名,则向其父类查找,依次向上递归,直至函数名匹配成功,然后进行参数类型等信息的匹配;或者查到最顶层仍未匹配到相应的函数名。
所以,当我们在派生类中没有重写重载函数之一的时候,在派生类中调用的重载函数是在其基类中查到的,因此,调用可以成功;然而,当我们仅重写了其中的一个重载函数时,在做函数名匹配时,在本类中就可以匹配到了,就不会向其父类查找了。而在派生类中,仅记录了这个被重写的函数的信息,当然也就没有另外两个重载函数的一些了,因此就导致了上述错误的出现了。 换句话说,派生类中的函数会将其父类中的同名函数屏蔽掉。
因此,如果派生类想通过自身类型使用的基类中重载版本,则派生类必须要么重定义所有重载版本,要么一个也不重定义。
那么,如果在派生类中需要且仅需要重写其中一个重载函数,必须得把其它重载函数都重定义吗?有没有简便的方法,仅重定义我们想要改变的那个,其它的还是从父类继承呢。答案是肯定的,有,而且还可以有不同的方式:
一、通过using在派生类中为父类函数成员提供声明:
前面知道,因为派生类重写的函数名屏蔽了父类中的同名函数,那么我们可以通过using来为父类函数提供声明;这样,派生类不用重定义所继承的每一个基类版本,它可以为重载成员提供 using声明。一个 using 声明只能指定一个名字,不能指定形参表,因此,为基类成员函数名称而作的 using 声明将该函数的所有重载实例加到派生类的作用域。将所有名字加入作用域之后,派生类只需要重定义本类型确实必须定义的那些函数,对其他版本可以使用继承的定义。
上面说了那么多,不知道说明白了没有,不过,看了下面的例子,你就会豁然开朗: so easy!
class Derived : public Base { public: using Base::print; void print() { cout << "print() in Derived." << endl; } };
仅仅需要加入 using Base::print; 问题便解决了:
【运行结果】
print() in Derived. print(int a) in Base. print(string s) in Base.
二、通过基类指针调用
在调用被屏蔽的重载函数时,可以不直接通过派生类对象调用,而是通过基类指针指向派生类对象,通过基类指针进行调用,这样就会直接在基类中进行查找函数名,从而可以匹配并进行类型匹配。
int main() { Derived d; Base* bp = &d; d.print(); bp->print(10); bp->print(""); return 0; }
【运行结果】
print() in Derived. print(int a) in Base. print(string s) in Base.
但是这样就有两种调用方式,看起来很不舒服,而且容易弄错。那么把在派生类中需要重载的那个版本相应地在基类中声明为vitual,从而可以实现动态绑定,就能统一的使用基类指针来调用了:
【参考代码】
class Base { public: virtual void print() { cout << "print() in Base." << endl; } void print(int a) { cout << "print(int a) in Base." << endl; } void print(string s) { cout << "print(string s) in Base." << endl; } }; class Derived : public Base { public: void print() { cout << "print() in Derived." << endl; } }; int main() { Derived d; Base* bp = &d; bp->print(); bp->print(10); bp->print(""); return 0; }
【运行结果】
print() in Derived. print(int a) in Base. print(string s) in Base.
原创文章,转载请注明: 转载自
IIcyZhao's Road
本文链接地址: http://blog.csdn.net/iicy266/article/details/11906697
相关文章推荐
- C# 调用 C++ dll 函数 时传递字符串 需要注意的问题
- C++指针作为函数的参数进行传递时需要注意的一些问题
- C# 调用 C++ dll 函数 时传递字符串 需要注意的问题
- C# 调用 C++ dll 函数 时传递字符串 需要注意的问题(zz)
- C++学习总结_成员函数的声明和实现需要注意问题
- C++基类成员函数访问派生类对象的基类成员问题
- c++ 基类设计 需要注意的几点
- C++多继承中重写不同基类中相同原型的虚函数
- mysql 聚集函数需要注意的问题
- linux环境下c++的编写、调试以及makefile多文件连编需要注意的问题
- C++使用localtime函数需要注意的地方
- C++程序员需要注意的11个问题
- C++基类与派生类的函数调用情况
- C++动态联编的好处,为什么要用基类的指针指向派生类的方式来实现重写
- Windows下使用创建多层文件夹 SHCreateDirectoryEx 函数需要注意的问题
- 字符串处理strlen函数需要注意的一些小细节问题
- ASP.NET MVC中重写IAuthorizationFilter的需要注意的问题
- 指针作为函数参数传递时需要注意的问题
- c#程序调用c++编写dll需要注意问题
- linux系统对时间函数的操作需要注意的问题