当一个名字在派生类的作用域没有找到时,编译器会去其相应的基类的作用域中寻找。
指针与引用的静态类型决定了它们的行为(是指能调用哪些函数和访问哪些成员吧),当基类型的引用或指针实际指向派生类的对象时,通过该指针或引用也只能调用基类中的函数(虚函数例外)。
class base{
public:
void func(){cout<<"in base\n";}
};
class derived:public base{
public:
void func(){cout<<"in derived\n";}
};
int main(void) {
derived obj_d;
derived &ref_d=obj_d;
base &ref_b=obj_d;
ref_b.func();
ref_d.func();
cout<<"end in main\n";
return EXIT_SUCCESS;
}
|
运行结果
in base
in derived
end in main
|
如果我们将基类中的func函数注释掉,则编译会报错
no matching function for call to `base::func()'
可以看到,通过派生类引用调用的是派生类中的函数,说明当派生类与基类有相同的名字时,派生类的会隐藏掉基类中的版本,函数与数据成员都是如此。即使函数原型不同也是如此。将上例中基类的func函数改为
void func(int i){cout<<"in derived\n";}
|
需要接收一个整形参数,主函数为
int main(void) {
derived obj_d;
obj_d.func(3);
cout<<"end in main\n";
return EXIT_SUCCESS;
}
|
看通过这样的方式能否调用到基类中的版本,但是程序编译会报错
no matching function for call to `derived::func(int)'
candidates are: void derived::func()
原因在于编译器在派生类中找到了func这个名字,就不会再向上查找了,即使这是个重载函数,调用时发现这不是派生类中的函数版本,所以报错。只有当派生类中没有该函数的定义时,编译器才会继续向上查找func这个名字,在基类中发现,并调用它。
就像局部域中的函数不会重载(overload)全局域中的函数,只会覆盖它(override).
对于基类中的重载函数,派生类要想继续使用它们,要么在其中全部对它们进行定义并按照自己的需求进行修改,要么一个也不定义。如果只定义了其中的一部分,那么派生类只拥有其定义的这一部分。
class base{
public:
void func(){cout<<"in base\n";}
void func(int i){cout<<"in base and i=<<"<<i<<"\n";}
};
class derived:public base{
public:
void func(){cout<<"in derived\n";} };
int main(void) {
derived obj_d;
obj_d.func(3);
cout<<"end in main\n";
return EXIT_SUCCESS;
}
|
编译报错
no matching function for call to `derived::func(int)'
candidates are: void derived::func()
这样当基类中重载函数过多时,工作会很麻烦,如果在派生类中想拥有全部的重载函数,可以使用
using语句。
class derived:public base{
public:
using base::func;
};
|
这样派生类就拥有了全部的重载函数,只需根据需要修改相应的版本即可。
注意:using 语句使用函数名字时,不可带参数。
现在可以理解为什么当需要动态绑定时,派生类与基类中的虚函数必须函数原型相同,如果原型不同,通过基类的指针或引用调用时,只会调用到基类中的版本。
class base{
public:
virtual void func(){cout<<"in base\n";}
};
class derived:public base{
public:
void func(int i){cout<<"in derived\n";}
};
int main(void) {
derived obj_d;
base &ref_b=obj_d;
ref_b.func();
cout<<"end in main\n";
return EXIT_SUCCESS;
}
|
派生类修改了基类中的虚函数,加上了整形参数,这时派生类中的func函数已经把基类中版本隐藏了,
同时派生类中的func函数已经不是虚函数了。c++中名字的查找过程为:
1.根据对象,指针,引用的静态版本决定哪一个函数被调用。
2.到相应的类中查找,如果没有找到,则到该类的直接基类中查找,按此过程下去都没找到,报错。
3.找到,做原型检查,看调用是否正确。
4.正确,判断是否动态绑定,调用相应版本。
纯虚函数与抽象类
如有一个汽车的类,而我们要操作的是轿车,卡车等其子类,同时又不希望产生出汽车类的对象以避免错误,怎么办?c++中的抽象类与纯虚函数应运而生。
当一个类我们只让其作为中间基类而使用它所派生出的其他类时,可以将其定义为
抽象类。
只需在该类中定义一个虚函数,并在其原型后加上"=0"
class base{
public:
virtual void func()=0;
};
class derived:public base{
public:
void func(){cout<<"in derived\n";}
};
int main(void) {
base obj_b;
cout<<"end in main\n";
return EXIT_SUCCESS;
}
|
企图创建一个base的对象,编译器报错
In function `int main()':
cannot declare variable `obj_b' to be of type `base'
because the following virtual functions are abstract:
virtual void base::func()
但是可以创造其子类的对象
int main(void) {
derived obj_d;
cout<<"end in main\n";
return EXIT_SUCCESS;
}
|
注意:在抽象类的子类中一定要有对抽象类中纯虚函数的重写,哪怕只有空的函数体,否则是创建不了抽象类的子类对象的!!!!!
阅读(815) | 评论(0) | 转发(0) |