C++语法小结: (2)
7. 虚函数在继承层次的作用域“如果通过基类的引用或者指针调用虚函数成员,那么编译器将在基类中查找函数名。假定找到了函数名,编译器就检查实参与形参是否匹配。 这也是虚函数必须在基类和派生类中拥有同一原型的原因。”
如果在派生类中定义了与基类虚函数同名但函数原型不同的函数,那么该函数将屏蔽基类定义的虚函数。
总之, (1)无论调用虚函数还是非虚函数,都是在编译时期根据调用者的静态类型来进行名字查找与函数类型检查。
(2)派生类的作用域嵌套在基类的作用域中,派生类可以重写(覆盖)基类定义的虚函数,也可以屏蔽基类的虚函数或者非虚函数。
如下示例:
class Cls1_t {
public:
virtual void PrintSelf() {cout << "I'm Cls1_t::PrintSelf()" << endl;}
};
class Cls2_t: public Cls1_t {
public:
void PrintSelf(int n) {cout << "I'm Cls2_t::PrintSelf(int )" << endl;}
};
int main(){
Cls1_t obj1;
Cls2_t obj2;
obj1.PrintSelf();
obj2.PrintSelf(100);
// 名字查找与函数类型匹配检查在编译时期根据调用者的静态类型确定,
// PrintSelf(int)屏蔽了基类部分的虚函数PrintSelf(),
// 因此该调用在编译期间出错
//obj2.PrintSelf();
Cls1_t *pBase;
pBase = &obj2;
// 在编译时期进行名字查找与检查,所以该调用合法。
// 因为Cls2_t没有覆盖PrintSelf()的定义,将调用其基类Cls1_t中的版本
pBase->PrintSelf();
}
8. 多继承
“在多重继承下,派生类的指针或引用可以转换为其任意基类的指针或者引用”。如下:
struct Cls11_t{ void PrintSelf() { cout << "I'm in Cls11_t" <<endl;} };
struct Cls21_t: virtual public Cls11_t{
void PrintSelf() { cout << "I'm in Cls21_t" <<endl;}
};
struct Cls22_t: virtual public Cls11_t{
void PrintSelf() { cout << "I'm in Cls22_t" <<endl;}
};
struct Cls31_t: public Cls21_t, public Cls22_t{};
void fcn1(Cls11_t &obj){ obj.PrintSelf();}
void fcn2(Cls21_t &obj){ obj.PrintSelf();}
void fcn3(Cls22_t &obj){ obj.PrintSelf();}
int main(){
Cls31_t obj31;
fcn1(obj31); fcn2(obj31); fcn3(obj31);
}
程序将输出:
I'm in Cls11_t
I'm in Cls21_t
I'm in Cls22_t
9. 当实参为数组类型时,函数模板形参形式对其类型的影响
9.1 实参为数组类型时,普通函数形参形式对其类型的影响
数组类型为 const 指针,且附带元素的类型和数组长度信息,仅当数组元素的类型和数组长度均相同时,两个数组类型才相同。比如,有“int a, b”,a,b不是同一个类型,因为数组的长度不同。
当实参为数组类型时,比如有“int a;”,以 a 作为实参,如果形参是非引用类型,比如“int pa”,N为值任意的常量表达式,那么编译器将 pa 处理为 int * 类型,而不是数组类型 int 。如果形参是引用类型,比如 “int (&pa)”,那么编译器将 pa 处理为 数组类型:int ,这时编译器会检查实参与形参类型是否匹配,即这两个数组类型的元素类型数组长度必须相同,否则实参与形参类型不匹配导致编译出错。
示例如下:
void fcn1_1(int (&a)) { // a 的类型为 int 的数组类型
cout << "fcn1_1(): sizeof(a)=" << sizeof(a) << endl;
for (int i=0; i<16; i++) cout << a << " ";
cout << endl;
}
void fcn1_2(int (&a)) { // a 的类型为 int 的数组类型
cout << "fcn1_2(): sizeof(a)=" << sizeof(a) << endl;
for (int i=0; i<15; i++) cout << a << " ";
cout << endl;
}
void fcn1_3(int (&a)) { // a 的类型为 int 的数组类型
cout << "fcn1_3(): sizeof(a)=" << sizeof(a) << endl;
for (int i=0; i<17; i++) cout << a << " ";
cout << endl;
}
void fcn2_1(int a) { // a 的类型为 int *类型
cout << "fcn2_1(): sizeof(a)=" << sizeof(a) << endl;
for (int i=0; i<16; i++) cout << a << " ";
cout << endl;
}
void fcn2_2(int a) { // a 的类型为 int *类型
cout << "fcn2_2(): sizeof(a)=" << sizeof(a) << endl;
for (int i=0; i<15; i++) cout << a << " ";
cout << endl;
}
void fcn2_3(int a) { // a 的类型为 int *类型
cout << "fcn2_3(): sizeof(a)=" << sizeof(a) << endl;
for (int i=0; i<17; i++) cout << a << " ";
cout << endl;
}
int main(){
int a;
for (int i=0; i<16; i++) a=i+1;
cout << "main(): sizeof(a)=" << sizeof(a) << endl;
fcn1_1(a); //形参类型和实参类型均为int 的数组类型,类型匹配
// fcn1_2(a); //形参类型为int ,实参类型为 int ,类型不匹配,编译出错
// fcn1_3(a); //形参类型为int ,实参类型为 int ,类型不匹配,编译出错
fcn2_1(a); //形参类型为 int*,可以讲数组类型转化为 int*
fcn2_2(a); //同上
fcn2_3(a); //同上
}
9.2 实参为数组类型时,函数模板形参形式对其类型的影响
与普通函数类似,当函数模板中函数形参为引用类型时,编译器将形参作为数组类型处理(数组类型附带数组元素类型和数组长度信息),当
template <typename T>
void PrintAry(const T &ary) { // ary 为数组类型
cout << "sizeof(ary)=" << sizeof(ary) << endl;
}
template <typename T>
void PrintAry2(const T ary) { // ary 为指针类型
cout << "sizeof(ary)=" << sizeof(ary) << endl;
}
template <typename T, int N>
void PrintAry3(const T (&ary)) { // ary 为数组类型,且 N 为数组中元素的个数
cout << "sizeof(ary)=" << sizeof(ary) << ", N=" << N << endl;
}
template <typename T>
void PrintAry4(const T ary) { // ary 为指针类型
cout << "sizeof(ary)=" << sizeof(ary) << endl;
}
template <typename T, int N>
void PrintAry5(const T ary) {
cout << "sizeof(ary)=" << sizeof(ary) << ", N=" << N << endl;
}
int main(){
int a;
PrintAry(a);
PrintAry2(a);
PrintAry3(a);
//PrintAry5(a);
}
程序输出:
sizeof(ary)=32
sizeof(ary)=4
sizeof(ary)=32, N=8
sizeof(ary)=4
PrintAry(),PrintAry4()与普通函数的情形类似,形参为引用类型,则被处理为数组类型,否则,被处理为指针类型。PrintAry2()实际上也与普通函数情类似,此时T实参类型为int *,而在 PrintAry4()中,T的实参类型为int。PrintAry3()实际上也与普通函数的情形类似,此时T的实参类型为int,而PrintAry()中T的实参类型为int。PrintAry5()是错误的,因为ary为非引用类型,被编译器当做指针类型处理,指针类型是不附带数组长度信息的,因此,编译器无法计算出N值。
在实参为数组类型的情况下,在函数模板中,仍就遵循引用类型的形参为数组类型,非引用类型的形参为指针类型,只是模板的类型形参可能不同,因此模式比普通函数的情况多一些。
10. dynamic_cast 相关
dynamic_cast<Derived_t *>(Base_t *pObj):用于在运行时检查基类指针pObj所指的对象,如果该对象为Derived_t类型,则将其地址转换为Derived_t *类型。 但是,如果像以下那样调用dynamic_cast,那么dynamic_cast总会调用成功,
dynamic_cast< Base_t *>(Base_t *pObj),不论在运行过程中pObj指向的是Base_t类型的对象,还是其派生类的对象,dynamic_cast总能成功调用,所以这样调用并没有意义。示例如下:
struct Base_t {
virtual void SameType( const Base_t &obj) {
if (const Base_t *pBase = dynamic_cast<const Base_t *>(&obj))
cout << "Base_t: Same type" << endl;
else
cout << "Base_t: Different type" << endl;
}
};
struct Derived_t: Base_t {
virtual void SameType( const Base_t &obj) {
if (const Derived_t *pDerived = dynamic_cast<const Derived_t *>(&obj))
cout << "Derived_t: Same type" << endl;
else
cout << "Derived_t: Different type" << endl;
}
};
int main() {
Base_t bobj;
Derived_t dobj;
bobj.SameType(dobj);
dobj.SameType(bobj);
}
运行结果为:
Base_t: Same type
Derived_t: Different type
可以将Base_t::SameType()的if子句改为:if (typeid(obj) == typeid(Base_t)),以区分基类对象和派生类对象的类型。
页:
[1]