如何停止隐式转换为虚函数
How to stop implicit conversion to virtual function
struct A{
virtual void fun(){cout<<"A";}
};
struct B:public A{
void fun(){cout<<"B";}
};
struct C:public B{
void fun(){cout<<"C";}
};
int main()
{
C c;B b1;
A *a=&b1;
a->fun(); //1
B *b=&c;
b->fun(); //2
return 0;
}
在上面的代码中,B::fun(( 隐式转换为虚拟函数,因为我使 A::fun(( 虚拟。我可以停止此转换吗?
如果不可能,有什么替代方案可以使上面的代码打印"BB"?
虚函数在所有派生类中都是虚拟的。没有办法阻止这种情况。
(§10.3/2 C++11( 如果在类 Base 中声明了虚拟成员函数 vf,并且在直接或间接派生自 Base 的类 Derived 中声明了与 Base::vf 具有相同名称、参数类型列表 (8.3.5(、cv 限定符和 ref 限定符(或不存在相同(的成员函数 vf,则 Derived::vf 也是虚拟的(无论它是否如此声明(,并且它覆盖了 Base::VF。为了方便起见,我们说任何虚拟函数都会覆盖自身。
但是,如果您想使用对应于指针的静态而不是动态类型的函数(即,在您的示例中,B::fun
而不是C::fun
,因为指针被声明为 B*
(,那么至少在 C++11 中,您可以使用下面的别名定义来访问静态(=编译时(类型:
template <typename Ptr>
using static_type = typename std::remove_pointer<Ptr>::type;
这就是您在main()
(或其他任何地方(使用它的方式:
int main()
{
C c; B b1;
A *a = &b1;
a->fun();
B *b = &c;
/* This will output 'B': */
b->static_type<decltype(b)>::fun();
return 0;
}
- 如果不希望派生类重写函数,则没有理由将其标记为基类
virtual
。将函数标记为virtual
的基础是通过派生类函数覆盖来具有多态行为。
好读:
何时将C++中的函数标记为虚拟函数?
- 如果您希望代码防止派生类中的意外覆盖。您可以使用 C++11 中的最终说明符。
是的,如果要显式调用特定类中的函数,可以使用完全限定名。
b->A::fun();
这将调用属于A
的fun()
的版本。
以下内容实现了您要求的可观察行为。 在A
中,非virtual
fun()
运行虚拟fun_()
,因此行为可以在B
中自定义,但是任何在派生类上调用fun()
的人都只能看到非多态版本。
#include <iostream>
using namespace std;
struct A{
void fun(){fun_();}
private:
virtual void fun_() { cout << "An"; }
};
struct B:public A{
void fun(){cout<<"Bn";}
private:
virtual void fun_() final { fun(); }
};
struct C:public B{
void fun(){cout<<"Cn";}
};
int main()
{
C c;B b1;
A *a=&b1;
a->fun(); //1
B *b=&c;
b->fun(); //2
c.fun(); // notice that this outputs "C" which I think is what you want
}
如果使用 C++03,您可以简单地省略"final"关键字 - 它只是为了防止在B
派生类(如 C
(中进一步不需要的覆盖虚拟行为。
(您可能会发现将其与"非虚拟接口模式"进行对比很有趣 - 请参阅Sutter和Alexandrescu的C++编码标准,第39点(
讨论
A
具有fun
虚拟意味着在派生类中重写它是派生类的必要自定义功能,但在派生层次结构中的某个时刻,实现行为的选择可能已缩小到 1,并提供final
实现并非不合理。
我真正担心的是你隐藏A
/B
C::fun
的fun()
......这很麻烦,因为如果他们做不同的事情,那么你的代码可能很难推理或调试。 B
决定最终确定虚拟功能意味着确定不需要这种进一步的定制。 从 A*
/A&
/B*
/B&
工作的代码将做一件事,而无论C
对象的类型是静态已知的,行为都可能不同。 模板化代码是一个可以轻松调用C::fun
的地方,而模板作者或用户却没有非常清楚它。 要评估这对您来说是否是一个真正的危险,了解"乐趣"的功能目的是什么以及A
、B
和C
之间的实现有何不同......
如果你像这样在 B 中声明函数
void fun(int ignored=0);
它将成为不参与解析虚拟呼叫的过载。请注意,即使a
实际上指的是B
,调用a->fun()
也会调用A::fun()
,所以我强烈建议不要使用这种方法,因为它会使事情变得比必要的更加混乱。
问题是:你到底想实现或避免什么?知道了这一点,这里的人们可以提出更好的方法。
- 如何使用Rcpp将R函数转换为C++函数
- C++函数转换为 C# 函数
- 将 C 函数转换为 C++ 以检查数字是否有效
- C++:从重载函数转换为 std::function
- 将 C 函数转换为C++语言
- C++隐式构造函数转换,后跟类型向上转换
- 将 lambda 函数转换为具有混合 lambda 引入器和参数列表的函子结构
- 通过 Boost Python 将 Python 函数转换为 C++,用作回调
- 如何将 MATLAB 图像处理库内置函数转换为 MATLAB 编码器代码生成不支持的 C++?
- 如何将函数转换为 lambda 函数
- 自定义函数转换错误?
- 将 C# 哈希函数转换为C++
- 将 lambda 函数转换为另一个编译单元中的普通函数会缩短编译时间吗?
- 加快 R 性能或将 R 函数转换为C++函数
- 将成员函数转换为指向成员函数的指针
- 将MATLAB炒作函数转换为C
- Rcpp - 用二进制函数转换数字向量?
- C lambda函数转换误差
- 此语法中的构造函数转换错误
- 将函数转换为find_if lambda