一、派生类的作用域嵌套在其基类的作用域之内
class Quote {
public:
double isbn() {
return 3.14;
}
};
class Disc_quote :public Quote {};
class Bulk_quote :public Disc_quote {};
int main()
{
Bulk_quote bulk;
cout << bulk.isbn();
return 0;
}
- bulk对象在调用isbn()函数时,会:
- 从Bulk_quote类中查找是否有该函数,发现没有
- 进一步查找Disc_quote类,发现Disc_quote类中也没有该函数
- 进一步查找Quote,发现存在,于是执行Quote中的isbn()函数
二、在编译时进行名字查找
- 一个对象、引用、或指针的静态类型决定了该对象的哪些成员是可见的。即使静态类型与动态类型可能不一致(例如使用基类的指针/引用指向于派生类)
- 举个例子:
class Quote {};
class Disc_quote :public Quote {
public:
std::pair<size_t, double> discount_policy()const {}
};
class Bulk_quote :public Disc_quote {};
int main()
{
Bulk_quote bulk;
Bulk_quote *bulkP = &bulk;
Quote *itemP = &bulk;
bulkP->discount_policy(); //正确,bulkP的类型是Bulk_quote
itemP->discount_policy(); //错误,itemP的类型是Quote
return 0;
}
- 上述代码错误的那一行是因为使用基类的指针指向于派生类,但是通过这个指针调用了属于派生的成员(但是基类中没有定义),该成员对于基类来说是不可见的
三、隐藏
四、虚函数与隐藏
- 通过隐藏的知识点我们知道,虚函数为什么在基类与派生类中的参数列表必须一致了。如果参数列表不一致,那么基类的虚函数在派生类中没有被重写,而是被隐藏了(就无法通过基类的引用或指针调用派生类的虚函数了)
演示案例
class Base {
public:
virtual int fcn(); //虚函数
};
class D1 :public Base {
public:
//因为参数列表不一致,因此Base的虚函数没有被覆盖(重写),而是被隐藏了
//所以下面的fcn函数时D1的一个普通成员函数,不是虚函数
int fcn(int);
virtual void f2(); //虚函数
};
class D2 :public D1 {
public:
int fcn(int); //非虚函数,隐藏了D1::fcn(int)
int fcn(); //覆盖了Base的虚函数fcn()
void f2(); //覆盖了D1的虚函数f2()
};
int main()
{
Base bobj;
D1 d1obj;
D2 d2obj;
Base *pb1 = &bobj;
Base *pb2 = &d1obj;
Base *pb3 = &d2obj;
pb1->fcn(); //虚调用,运行时调用Base::fcn()
pb2->fcn(); //虚调用,运行时调用Base::fcn()
pb3->fcn(); //虚调用,运行时调用D2::fcn()
D1 *d1p = &d1obj;
D2 *d2p = &d2obj;
d1p->f2(); //虚调用,运行时调用D1::f2()
d2p->f2(); //虚调用,运行时调用D2::f2()
return 0;
}
int main()
{
D2 d2obj;
Base *p1 = &d2obj;
D1 *p2 = &d2obj;
D2 *p3 = &d2obj;
//错误p1->fcn(42); Base没有用fcn(int)函数
p2->fcn(42); //静态绑定,调用D1::fcn(int)
p3->fcn(42); //静态绑定,调用D2::fcn(int)
return 0;
}
五、覆盖重载的函数
- 从上面的隐藏知识点来看,对于派生类的函数,不论是成员函数还是虚函数都可以被重载
- 有时一个基类有很多重载函数,而派生类重写了基类的函数,那么默认情况下,基类中的这些所有的重载函数都不可以使用了,见下面的演示案例:
class A {
public:
void func();
void func(int);
void func(string);
};
class B :public A {
public:
void func(); //此时A中的三个func都被覆盖了,只能使用无参数的func斑斑
};
int main()
{
B b;
b.func(); //正确
//b.func(10); 错误
//b.func("HelloWorld"); 错误
return 0;
}
- 如果我们希望只覆盖基类中的一部分函数,而其他函数在派生类中还没有被覆盖,一种使用方法就是使用前面文章介绍的using声明。这样的话就无须覆盖基类中的每一个重载版本了
- using声明只需要给出名称,而不需要给出参数列表,因此基类中的所有重载函数在派生类中都可以使用了
- 演示案例:
class A {
public:
void func();
void func(int);
void func(string);
};
class B :public A {
public:
void func();
//使用using声明,此时只有无参数的func被覆盖了,而另外两种func在派生类中没有被覆盖
using A::func;
};
int main()
{
B b;
b.func(); //正确
b.func(10); //正确
b.func("HelloWorld");//正确
return 0;
}