调用虚函数的逻辑不清晰(或者是方法隐藏?)
The logic of invoking virtual functions is not clear (or it is method hiding?)
(在 msvc2017 上测试)
struct AAA
{
virtual float run(int arg)
{
return 5.5f;
}
};
struct BBB : AAA
{
virtual bool run(double arg)
{
return false;
}
};
struct CCC : BBB
{
virtual float run(int arg)
{
return 7.7f;
}
virtual bool run(double arg)
{
return true;
}
};
CCC c;
BBB* pb = &c;
pb->run(5); // call CCC::run(double arg), WHY??
pb->run((int)5); // call CCC::run(double arg), WHY??
为什么 pb->run(5)
只调用 CCC::run(double arg)
,而不调用 CCC::run(int arg)
?
具有不同签名的子 class 的虚方法是否与基 class 的接口重叠?
当你
struct BBB : AAA
{
virtual bool run(double arg)
{
return false;
}
};
run
与 AAA
中的 run
有不同的签名。这意味着 BBB::run(double)
将隐藏 AAA::run(int)
。既然如此,您唯一可以从 BBB
调用的 run
是 bool run(double arg)
。当你这样做时
pb->run(5);
它找到 bool BBB::run(double arg)
,因为这是唯一可以从 BBB
静态调用的函数,然后虚拟调度开始调用 CCC::run(double)
为了得到要调用的函数的int
版本,需要将int
版本带入BBB
。您可以通过编写一个来做到这一点,或者您可以使用 using AAA::run;
将其导入。执行其中任何一个都会使 pb->run(5);
从 [= 调用 run
的 int
版本30=].
不要忘记,在使用多态性时,您应该将顶级析构函数(在本例中为 AAA
)声明为虚拟的。这允许您在使用动态分配时正确删除对象。有关详细信息,请参阅:When to use virtual destructors?
一切都很简单。
class BBB 实际上有两个虚函数。一个在其基数 class AAA
中声明
struct AAA
{
virtual float run(int arg)
{
return 5.5f;
}
};
其他在 class BBB 本身中声明。
struct BBB : AAA
{
virtual bool run(double arg)
{
return false;
}
};
class BBB 中声明的函数隐藏了class AAA 中声明的函数。 (在派生 class 中声明的任何名称都隐藏了在派生 class 的基 class 中声明的同名实体)
在 class CCC 中,两个函数都被覆盖了。
这些函数调用
pb->run(5); // call CCC::run(double arg), WHY??
pb->run((int)5); // call CCC::run(double arg), WHY??
没有区别,因为它们的参数类型为 int
。
指针pb
的静态类型是BBB *
。因此编译器在 class BBB 中搜索名称 运行。
在 class 中只有一个同名函数可见。它是在 class
中声明的函数
virtual bool run(double arg)
{
return false;
}
因此编译器 运行 使用此签名来调用此虚函数,但使用为 class CCC 定义的 table 虚函数指针调用它,因为指针的动态类型pb
是 CCC *
.
您可以通过 using
声明使 class AAA 中声明的函数在 class BBB 中可见。例如
struct BBB : AAA
{
using AAA:: run;
virtual bool run(double arg)
{
return false;
}
};
在这种情况下,函数声明(在 class AAA 中声明)也是 class BBB 中的成员声明。那就是 class BBB 将有两个重载的不同虚函数的声明。
这是一个演示程序
#include <iostream>
struct AAA
{
virtual float run(int arg)
{
return 5.5f;
}
};
struct BBB : AAA
{
using AAA:: run;
virtual bool run(double arg)
{
return false;
}
};
struct CCC : BBB
{
virtual float run(int arg)
{
return 7.7f;
}
virtual bool run(double arg)
{
return true;
}
};
int main()
{
CCC c;
BBB* pb = &c;
std::cout << pb->run(5) << '\n';
std::cout << pb->run(5.6 ) << '\n';
return 0;
}
它的输出是
7.7
1
为了使派生 class 及其基础 class 中成员声明的情况更清楚,请考虑块范围的类似情况。
这是一个演示程序
#include <iostream>
void f( int ) { std::cout << "void f( int )\n"; }
void f( double ) { std::cout << "void f( double )\n"; }
int main()
{
void f( double );
f( 5 );
f( 5.5 );
return 0;
}
函数f
在函数块范围内的内部声明main
隐藏了函数在全局范围内的其他声明。
程序输出为
void f( double )
void f( double )
(在 msvc2017 上测试)
struct AAA
{
virtual float run(int arg)
{
return 5.5f;
}
};
struct BBB : AAA
{
virtual bool run(double arg)
{
return false;
}
};
struct CCC : BBB
{
virtual float run(int arg)
{
return 7.7f;
}
virtual bool run(double arg)
{
return true;
}
};
CCC c;
BBB* pb = &c;
pb->run(5); // call CCC::run(double arg), WHY??
pb->run((int)5); // call CCC::run(double arg), WHY??
为什么 pb->run(5)
只调用 CCC::run(double arg)
,而不调用 CCC::run(int arg)
?
具有不同签名的子 class 的虚方法是否与基 class 的接口重叠?
当你
struct BBB : AAA
{
virtual bool run(double arg)
{
return false;
}
};
run
与 AAA
中的 run
有不同的签名。这意味着 BBB::run(double)
将隐藏 AAA::run(int)
。既然如此,您唯一可以从 BBB
调用的 run
是 bool run(double arg)
。当你这样做时
pb->run(5);
它找到 bool BBB::run(double arg)
,因为这是唯一可以从 BBB
静态调用的函数,然后虚拟调度开始调用 CCC::run(double)
为了得到要调用的函数的int
版本,需要将int
版本带入BBB
。您可以通过编写一个来做到这一点,或者您可以使用 using AAA::run;
将其导入。执行其中任何一个都会使 pb->run(5);
从 [= 调用 run
的 int
版本30=].
不要忘记,在使用多态性时,您应该将顶级析构函数(在本例中为 AAA
)声明为虚拟的。这允许您在使用动态分配时正确删除对象。有关详细信息,请参阅:When to use virtual destructors?
一切都很简单。
class BBB 实际上有两个虚函数。一个在其基数 class AAA
中声明struct AAA
{
virtual float run(int arg)
{
return 5.5f;
}
};
其他在 class BBB 本身中声明。
struct BBB : AAA
{
virtual bool run(double arg)
{
return false;
}
};
class BBB 中声明的函数隐藏了class AAA 中声明的函数。 (在派生 class 中声明的任何名称都隐藏了在派生 class 的基 class 中声明的同名实体)
在 class CCC 中,两个函数都被覆盖了。
这些函数调用
pb->run(5); // call CCC::run(double arg), WHY??
pb->run((int)5); // call CCC::run(double arg), WHY??
没有区别,因为它们的参数类型为 int
。
指针pb
的静态类型是BBB *
。因此编译器在 class BBB 中搜索名称 运行。
在 class 中只有一个同名函数可见。它是在 class
中声明的函数virtual bool run(double arg)
{
return false;
}
因此编译器 运行 使用此签名来调用此虚函数,但使用为 class CCC 定义的 table 虚函数指针调用它,因为指针的动态类型pb
是 CCC *
.
您可以通过 using
声明使 class AAA 中声明的函数在 class BBB 中可见。例如
struct BBB : AAA
{
using AAA:: run;
virtual bool run(double arg)
{
return false;
}
};
在这种情况下,函数声明(在 class AAA 中声明)也是 class BBB 中的成员声明。那就是 class BBB 将有两个重载的不同虚函数的声明。
这是一个演示程序
#include <iostream>
struct AAA
{
virtual float run(int arg)
{
return 5.5f;
}
};
struct BBB : AAA
{
using AAA:: run;
virtual bool run(double arg)
{
return false;
}
};
struct CCC : BBB
{
virtual float run(int arg)
{
return 7.7f;
}
virtual bool run(double arg)
{
return true;
}
};
int main()
{
CCC c;
BBB* pb = &c;
std::cout << pb->run(5) << '\n';
std::cout << pb->run(5.6 ) << '\n';
return 0;
}
它的输出是
7.7
1
为了使派生 class 及其基础 class 中成员声明的情况更清楚,请考虑块范围的类似情况。
这是一个演示程序
#include <iostream>
void f( int ) { std::cout << "void f( int )\n"; }
void f( double ) { std::cout << "void f( double )\n"; }
int main()
{
void f( double );
f( 5 );
f( 5.5 );
return 0;
}
函数f
在函数块范围内的内部声明main
隐藏了函数在全局范围内的其他声明。
程序输出为
void f( double )
void f( double )