作者:gzshun. 原创作品,转载请标明出处!
来源:http://blog.csdn.net/gzshun
在继承过程中,从基类派生出派生类,可能出现重名的成员,包括数据成员或成员函数,这些属于作用域方面的内容。
一、基类的引用或指针访问派生类
基类的引用或指针只能访问派生类中属于基类的部分成员,不能访问派生类的部分,否则就会编译出错。
- #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函数版本。