1

在查看以下简单代码时,如果我知道我们没有从基指针中删除,那么引入虚拟析构函数是否有意义?出于性能原因,我们似乎应该尽量避免 vtable 查找。我了解过早优化等,但这只是一个一般问题。我想知道您对以下问题的看法:

  • 如果我们不通过基指针删除项目,则使用受保护的析构函数
  • 与引入单个虚拟方法相关的开销

此外,如果我的类只有析构函数作为虚拟方法,那么查找开销是否仅适用于析构函数方法,而其他方法不会产生惩罚,或者一旦引入 vptr,一切都会受到影响?我假设每个类内部都会有一个额外的 vptr,但它只需要在析构函数上执行 vptr 查找。

class CardPlayer
{
  public:
    typedef std::vector<CardPlayer> CollectionType;

    explicit CardPlayer()=default;

    explicit CardPlayer(const Card::CollectionType& cards);
    explicit CardPlayer(Card::CollectionType&& cards);

    void receiveCard(const Card& card);

    bool discardCard(Card&& card);
    void foldCards();

    inline const Card::CollectionType& getCards()  { return cards_; }
    // virtual ~CardPlayer() = default;  // should we introduce vtable if not really needed?
  protected:
    ~CardPlayer()=default;
    Card::CollectionType cards_;
};
--------------------------------------------------------------------
#include "CardPlayer.h"
#include <functional>

class BlackJackPlayer : public CardPlayer
{
  public:
    typedef std::vector<BlackJackPlayer> CollectionType;
    typedef std::function<bool(const Card::CollectionType&)> hitFnType;

  BlackJackPlayer(hitFnType fn) : hitFn_(fn) {}

  bool wantHit() 
  {
    return hitFn_(getCards());
  }

  hitFnType hitFn_;
};
4

3 回答 3

0

从另一个方向来看,如果您假设永远不会在基类上调用 delete,则无需将析构函数设为虚拟,并且隐藏基类析构函数是强制执行假设的好方法。

关于简单地使您的析构函数为虚拟,只要您声明任何虚拟方法,该类将自动获得一个 vtable,这会增加该类的内存占用。只有虚函数会导致 vtable 查找。但是,额外的内存(单个指针)和查找都具有最小的性能成本,因此您通常不必担心它,除非在性能最关键的应用程序中。

于 2013-10-06T16:20:36.670 回答
0

使析构函数受保护意味着它不能通过基类指针或引用来调用,这意味着它不需要是虚拟的。

一般来说,只有公共方法或基类中其他方法调用的方法才需要是虚拟的。受保护的方法只有在从类中的其他方法调用时才需要是虚拟的。

于 2013-10-06T19:23:50.200 回答
0

我会避免使用虚拟析构函数,因此在您的情况下将 vtbl 添加到类中。您可以通过基类指针保护该类不被删除,因此似乎没有任何其他虚拟方法,这将是过早的悲观:)

此外,每个实例多一个指针(vtbl)可以在大型项目中加起来。性能通常取决于内存访问,因此您应该保持对象大小尽可能小,并且您的内存访问模式尽可能本地化。vtbl 将位于不同的内存位置,在最坏的情况下,您要求处理器读取另一个缓存行只是为了删除一个对象。

要回答您遇到的另一个问题:只有虚拟方法通过 vtbl 路由,所有非虚拟调用都不受影响。

于 2013-10-06T16:14:57.887 回答