C++支持通过虚拟机制进行动态绑定.但据我所知,虚拟机制是编译器的实现细节,标准只是指定了在特定场景下应该发生的行为.大多数编译器通过虚拟表和虚拟指针实现虚拟机制.是的,我知道这是如何工作的,所以我的问题不是关于虚拟指针和表的实现细节.我的问题是:
sizeof只有一个虚函数的任何类的将是一个指针(vptr的内部尺寸this)上编译,所以考虑到虚拟PTR和TBL机制本身是编译器实现,将这个说法我在上面做永远是真的吗?是否有任何理由使重写的C++虚函数的权限与基类不同?这样做有危险吗?
例如:
class base {
public:
virtual int foo(double) = 0;
}
class child : public base {
private:
virtual int foo(double);
}
Run Code Online (Sandbox Code Playgroud)
在C++常见问题解答说,这是一个坏主意,但没有说为什么.
我已经在一些代码中看到了这个习惯用法,我相信作者试图让这个类最终,基于一个假设,即不可能覆盖私有成员函数.但是,本文显示了重写私有函数的示例.当然,C++ faq的另一部分建议不要这样做.
我的具体问题:
在派生类和基类中使用不同的虚拟方法权限是否存在任何技术问题?
有没有合理的理由这样做?
在C++中使用接口(抽象基类)时是否存在运行时性能损失?
如果我想使类适应,并且可以从外部选择不同的算法 - C++中最好的实现是什么?
我主要看到两种可能性:
这是一个小例子,在各种版本中实现:
版本1:抽象基类
class Brake {
public: virtual void stopCar() = 0;
};
class BrakeWithABS : public Brake {
public: void stopCar() { ... }
};
class Car {
Brake* _brake;
public:
Car(Brake* brake) : _brake(brake) { brake->stopCar(); }
};
Run Code Online (Sandbox Code Playgroud)
版本2a:模板
template<class Brake>
class Car {
Brake brake;
public:
Car(){ brake.stopCar(); }
};
Run Code Online (Sandbox Code Playgroud)
版本2b:模板和私有继承
template<class Brake>
class Car : private Brake {
using Brake::stopCar;
public:
Car(){ stopCar(); }
};
Run Code Online (Sandbox Code Playgroud)
来自Java,我自然倾向于始终使用版本1,但模板版本似乎经常是首选,例如在STL代码中?如果这是真的,是因为内存效率等(没有继承,没有虚函数调用)?
我意识到版本2a和2b之间没有太大的区别,请参阅C++ …
c++ templates abstract-class design-patterns virtual-functions
我有一些经过大量优化的数学函数需要1-2 nanoseconds完成.这些功能每秒被称为数亿次,因此尽管性能已经非常出色,但呼叫开销仍是一个问题.
为了保持程序的可维护性,提供这些方法的类继承了一个IMathFunction接口,以便其他对象可以直接存储特定的数学函数并在需要时使用它.
public interface IMathFunction
{
double Calculate(double input);
double Derivate(double input);
}
public SomeObject
{
// Note: There are cases where this is mutable
private readonly IMathFunction mathFunction_;
public double SomeWork(double input, double step)
{
var f = mathFunction_.Calculate(input);
var dv = mathFunction_.Derivate(input);
return f - (dv * step);
}
}
Run Code Online (Sandbox Code Playgroud)
由于消费代码使用它,这种接口与直接呼叫相比造成了巨大的开销.一个直接调用需要1-2ns,而虚拟接口调用需要8-9ns.显然,接口的存在及其随后的虚拟呼叫转换是这种情况的瓶颈.
如果可能的话,我想保留可维护性和性能.有没有办法在实例化对象时将虚函数解析为直接调用,以便所有后续调用都能够避免开销?我认为这将涉及用IL创建委托,但我不知道从哪里开始.
假设我在抽象基类指针mypointer-> foo()上有一个虚函数调用foo().当我的应用程序启动时,根据文件的内容,它选择实例化一个特定的具体类,并将mypointer分配给该实例.对于应用程序的其余部分,mypointer将始终指向该具体类型的对象.我无法知道这个具体类型是什么(它可以由动态加载的库中的工厂实例化).我只知道在第一次生成具体类型的实例后,类型将保持不变.指针可能并不总是指向同一个对象,但该对象将始终具有相同的具体类型.请注意,类型在技术上是在'运行时'确定的,因为它基于文件的内容,但是在'启动'(加载文件)之后,类型是固定的.
但是,在C++中,每次在应用程序的整个持续时间内调用foo时,我都会支付虚函数查找成本.编译器无法优化查找,因为它无法知道具体类型在运行时不会发生变化(即使它是有史以来最令人惊奇的编译器,也无法推测动态加载的行为库).在JIT编译语言(如Java或.NET)中,JIT可以检测到反复使用相同类型并执行内联缓存.我基本上是在寻找一种方法来手动为C++中的特定指针执行此操作.
在C++中是否有任何方法可以缓存此查找?我意识到解决方案可能非常糟糕.我愿意接受ABI /编译器特定的黑客攻击,如果有可能编写发现ABI /编译器相关方面的配置测试,那么即使不是真正可移植的,它也"实际上是可移植的".
更新:对反对者:如果这不值得优化,那么我怀疑现代JIT会做到这一点.您是否认为Sun和MS的工程师正在浪费时间实施内联缓存,并没有对其进行基准测试以确保有所改进?
我有一个声明如下的抽象类:
class my_type {
public:
virtual ~my_type() = default;
virtual void do_something() = 0;
};
Run Code Online (Sandbox Code Playgroud)
使用default关键字声明这样的析构函数是一种良好的做法吗?有没有更好的办法?
另外,是= 0一种现代(C++ 11)指定无默认实现的方式,还是有更好的方法?
简而言之:从指向派生类实例的C++基类指针,如何在运行时确定是否已重新实现非纯虚函数(在基类中具有实现)派生类?
上下文:
我正在编写一个C++库来解决某些类别的数学方程.该库提供了一个Equation具有多个虚函数的类,库用户将其用作他们希望解决的特定等式的基类.该库还提供了一个Solver类,它接受Equation *一个构造函数参数.然后用户按以下方式编写代码:
class MyEquation : public Equation
{ ... } // equation definition here
int main()
{
MyEquation myEqn;
Solver solver(&myEqn);
solver.Solve();
}
Run Code Online (Sandbox Code Playgroud)
如果虚拟函数的某些组合Equation未在导出的方程类中重新定义,Solver则可以省略由对象运行的算法的某些计算上昂贵的部分.因此,我想知道,在构造函数中Solver,哪些函数已被重新定义,而是将运行默认实现Equation.
我想让这对图书馆的用户透明,所以我不是在寻找一个解决方案,例如,用户在派生方程的构造函数中设置一些标志,指定哪些函数已被重新定义.
一种可能的解决方案是在虚拟函数的默认实现中Equation设置类中的私有标志Equation; Solver然后,类的构造函数可以清除此标志,运行虚函数,并检查标志值以查看是否Equation已调用实现.我想避免这种情况,因为每次执行虚函数时只需设置标志就会使算法减慢很多(这些虚函数的执行对程序的运行时间有很大帮助,而默认实现只返回一个常数).
c++ ×9
c# ×1
c++11 ×1
caching ×1
destructor ×1
optimization ×1
overriding ×1
performance ×1
templates ×1
vptr ×1
vtable ×1