继承-虚函数-作用域
来源:互联网 发布:极限挑战网络直播 编辑:程序博客网 时间:2024/05/21 06:31
来源:http://blog.csdn.net/gzshun
在继承过程中,从基类派生出派生类,可能出现重名的成员,包括数据成员或成员函数,这些属于作用域方面的内容。
一、基类的引用或指针访问派生类
执行结果:
调用基类的成员函数
调用基类的成员函数
调用派生类的成员函数
执行结果:
犀利爹的年龄:120
犀利哥的年龄:100
三、重复的成员函数
基类与派生类若存在重复的成员函数,则有2种情况:
第一种情况:函数名相同,参数列表不同,如果将基类的成员函数的作用域引入派生类中,就是重载版本了
第二种情况:函数的所有方面都相同,包括函数名,参数列表与返回值
第一种情况:
函数名相同,参数列表不同,这跟函数的重载一样。但这并不是函数的重载,因为函数的重载必须在同一个作用域中定义,而基类和派生类定义了不同的作用域。在这里,可以使用using在派生类中声明基类的函数,将基类的函数的作用域引入派生类中的作用域,让这里的函数成为重载版本,来个小例子。
执行结果:
调用派生类的打印函数: val = 2
调用基类的打印函数: str = hello
这里如果没有"using CBase::Print;"的声明,那么将会编译错误。
第二种情况:
函数的所有方面都相同,包括函数名,参数列表与返回值,在派生类中,要调用基类的同名成员函数,方法跟同名数据成员的方法一样,格式是:"<类名>::<成员函数>"。
执行结果:
调用派生类的成员函数
调用基类的成员函数
四、虚函数的作用域
关于虚函数的内容,就会涉及到动态绑定的知识了,这里先不介绍,主要还是说明类继承的作用域的问题。在类的继承过程中,基类中的成员函数是不是虚函数,将会起着非常大的作用,先来看2个虚函数的例子。
1.在基类中没有使用virtual关键字
执行结果:
调用CBase::Print()
调用CBase::Print()
调用CBase::Print()
2.在基类中使用virtual关键字
执行结果:
调用CBase::Print()
调用CDerived1::Print()
调用CDerived2::Print()
1,2两个例子可以看到,一个virtual的关键字起着这么大的作用。当基类的成员函数使用virtual关键字修饰的话,基类指针会根据指向的对象的实际类型来寻找相应的类的成员函数的定义。要获得动态绑定,必须通过基类的引用或指针调用虚成员。
在继承过程中,从基类派生出派生类,可能出现重名的成员,包括数据成员或成员函数,这些属于作用域方面的内容。
一、基类的引用或指针访问派生类
基类的引用或指针只能访问派生类中属于基类的部分成员,不能访问派生类的部分,否则就会编译出错。
- #include <iostream>
- using namespace std;
- class CBase
- {
- public:
- void BasePrint() const
- {
- cout << "调用基类的成员函数" << endl;
- }
- };
- class CDerived : public CBase
- {
- public:
- void DerivedPrint() const
- {
- cout << "调用派生类的成员函数" << endl;
- }
- };
- int main()
- {
- CDerived obj;
- CBase *bObj = &obj;
- CDerived * dObj = &obj;
- bObj->BasePrint();
- // bObj->DerivedPrint();//错误,基类指针不能调用派生类的部分
- dObj->BasePrint();
- dObj->DerivedPrint();
- return 0;
- }
执行结果:
调用基类的成员函数
调用基类的成员函数
调用派生类的成员函数
二、重复的数据成员
有时基类和派生类有同名的数据成员,甚至可能在基类和间接基类中都有同名的数据成员。
这种情况一般会出现在:从 由另一个程序员创建的基类中派生自己的类,可能不知道在基类中有什么私有数据成员,此时自己又创建了与基类当中同名的数据成员。
当然这种情况是不会阻碍继承的,编译器做了很多的工作。但这里主要是介绍如何从派生类中调用派生类的同名私有成员或者基类的私有成员。
例子:
CBase是CDerived的父类,它们拥有一个同名成员为mVal。
在派生类中
1.默认是访问自己的数据成员mVal;
2.若要访问基类的mVal,要用这种格式"<类名>::<数据成员>", CBase::mVal
- #include <iostream>
- using namespace std;
- class CBase
- {
- public:
- CBase(int x = 120) : mVal(x) {}
- int mVal;
- };
- class CDerived : public CBase
- {
- public:
- CDerived(int x = 100) : mVal(x) {}
- void PrintValue() const
- {
- cout << "犀利爹的年龄:" << CBase::mVal << endl
- << "犀利哥的年龄:" << mVal << endl;
- }
- int mVal;
- };
- int main()
- {
- CDerived human;
- human.PrintValue();
- return 0;
- }
执行结果:
犀利爹的年龄:120
犀利哥的年龄:100
三、重复的成员函数
基类与派生类若存在重复的成员函数,则有2种情况:
第一种情况:函数名相同,参数列表不同,如果将基类的成员函数的作用域引入派生类中,就是重载版本了
第二种情况:函数的所有方面都相同,包括函数名,参数列表与返回值
第一种情况:
函数名相同,参数列表不同,这跟函数的重载一样。但这并不是函数的重载,因为函数的重载必须在同一个作用域中定义,而基类和派生类定义了不同的作用域。在这里,可以使用using在派生类中声明基类的函数,将基类的函数的作用域引入派生类中的作用域,让这里的函数成为重载版本,来个小例子。
- #include <iostream>
- using namespace std;
- class CBase
- {
- public:
- void Print(const char *str)
- {
- cout << "调用基类的打印函数: str = " << str << endl;
- }
- };
- class CDerived : public CBase
- {
- public:
- using CBase::Print;
- void Print(int val)
- {
- cout << "调用派生类的打印函数: val = " << val << endl;
- }
- };
- int main()
- {
- CDerived obj;
- obj.Print(2);
- obj.Print("hello");
- return 0;
- }
执行结果:
调用派生类的打印函数: val = 2
调用基类的打印函数: str = hello
这里如果没有"using CBase::Print;"的声明,那么将会编译错误。
第二种情况:
函数的所有方面都相同,包括函数名,参数列表与返回值,在派生类中,要调用基类的同名成员函数,方法跟同名数据成员的方法一样,格式是:"<类名>::<成员函数>"。
- #include <iostream>
- using namespace std;
- class CBase
- {
- public:
- void Print()
- {
- cout << "调用基类的成员函数" << endl;
- }
- };
- class CDerived : public CBase
- {
- public:
- void Print()
- {
- cout << "调用派生类的成员函数" << endl;
- }
- };
- int main()
- {
- CDerived human;
- human.Print();
- human.CBase::Print();
- return 0;
- }
执行结果:
调用派生类的成员函数
调用基类的成员函数
四、虚函数的作用域
关于虚函数的内容,就会涉及到动态绑定的知识了,这里先不介绍,主要还是说明类继承的作用域的问题。在类的继承过程中,基类中的成员函数是不是虚函数,将会起着非常大的作用,先来看2个虚函数的例子。
1.在基类中没有使用virtual关键字
- #include <iostream>
- using namespace std;
- class CBase
- {
- public:
- void Print() const
- {
- cout << "调用CBase::Print()" << endl;
- }
- };
- class CDerived1 : public CBase
- {
- public:
- void Print() const
- {
- cout << "调用CDerived1::Print()" << endl;
- }
- };
- class CDerived2 : public CDerived1
- {
- public:
- void Print() const
- {
- cout << "调用CDerived2::Print()" << endl;
- }
- };
- int main()
- {
- CBase bobj;
- CDerived1 d1obj;
- CDerived2 d2obj;
- CBase *bp1 = &bobj;
- CBase *bp2 = &d1obj;
- CBase *bp3 = &d2obj;
- bp1->Print();
- bp2->Print();
- bp3->Print();
- return 0;
- }
执行结果:
调用CBase::Print()
调用CBase::Print()
调用CBase::Print()
2.在基类中使用virtual关键字
- #include <iostream>
- using namespace std;
- class CBase
- {
- public:
- virtual void Print() const
- {
- cout << "调用CBase::Print()" << endl;
- }
- };
- class CDerived1 : public CBase
- {
- public:
- void Print() const
- {
- cout << "调用CDerived1::Print()" << endl;
- }
- };
- class CDerived2 : public CDerived1
- {
- public:
- void Print() const
- {
- cout << "调用CDerived2::Print()" << endl;
- }
- };
- int main()
- {
- CBase bobj;
- CDerived1 d1obj;
- CDerived2 d2obj;
- CBase *bp1 = &bobj;
- CBase *bp2 = &d1obj;
- CBase *bp3 = &d2obj;
- bp1->Print();
- bp2->Print();
- bp3->Print();
- return 0;
- }
执行结果:
调用CBase::Print()
调用CDerived1::Print()
调用CDerived2::Print()
1,2两个例子可以看到,一个virtual的关键字起着这么大的作用。当基类的成员函数使用virtual关键字修饰的话,基类指针会根据指向的对象的实际类型来寻找相应的类的成员函数的定义。要获得动态绑定,必须通过基类的引用或指针调用虚成员。
在第二个例子中,将Print函数声明为虚函数,这样子编译器会生成代码,在运行时基于引用或指针所绑定的对象的实际类型进行调用。bp2指向CDerived1对象,bp3指向CDerived2对象,所以都是调用属于自己的Print函数版本。
补充:
派生类虚函数调用基类版本时,必须显示调用作用域操作符。如果派生类函数忽略了这样做,则函数调用会在运行时确定并且将是一个自身调用,从而导致无穷递归。
class A{public: virtual void f1() = 0;}class B: public A{public: virtual void f1(); void f2();}派生类虚函数调用基类版本,相当于B中的f1调用A中的f1,如果不显示作用域,那么派生类中继承的虚函数会屏蔽基类中的虚函数,于是就相当于自己调用自己,就无穷递归了。如果你在f2中调用基类中的f1,不显示作用域就相当于调用B中的f1。
- 继承-虚函数-作用域
- JavaScript 函数、作用域和继承
- 《C++ Primer》读书笔记第十五章-2-虚函数、继承中的类作用域
- C++中继承类构造函数和作用域
- 【C++继承】之虚拟继承、作用域
- C++虚继承的作用
- C++虚继承的作用
- C++虚继承的作用
- C++虚继承的作用
- C++虚继承的作用
- 继承中的类作用域
- 继承中的类作用域
- Angulars Controller作用域继承
- 作用域层级和继承
- JS变量提升、匿名函数、原型继承、作用域、闭包机制等。
- c++基础10:继承和派生 虚函数的作用 多态性概念 纯虚函数和抽象类的概念
- 构造函数私有化的作用以及私有继承
- 虚函数 虚继承
- 如何避免Android内存泄漏之Context
- Android 初学---Android架构
- 输入框提示列表
- POJ 3020 最小边覆盖
- 如何避免Android内存泄漏——Context
- 继承-虚函数-作用域
- JSTL-核心标签库
- arm spinlock
- tomcat配置压缩JS代码
- 全排列递归算法
- lamp编译安装
- POJ 1459 基本输入题&&英文阅读题
- SVN命令行同步更新
- 性能分析工具 oprofile