Wednesday, February 16, 2022

Watch out for virtual function invocation

virtual function

 In some heavily invoked code the following construct was used:

interface ITable
{
   virtual std::string GetData() const = 0;
};

class Table : public ITable
{
public:
   virtual std::string GetData() const override
   {
      return std::string{"1"};
   }
};

class TableCt : public Table
{
public:
   long GetDataCt() const
   {
      const std::string strData = GetData();  // watch out
      return std::stol(strData);
   }
};

Client code invokes 'GetDataCt', e.g.:

void f()
{
    TableCt tbl;
    tbl.GetDataCt();  
}

in the real case TableCt wasn't the final class but GetData will not be overriden anywhere else. The code is functional correct but it takes a performance hit since base class function 'GetData' is invoked through vtable:

    TableCt tbl;
    tbl.GetDataCt();
00007FF653F710BC  lea         rdx,[rsp+30h]  
00007FF653F710C1  lea         rcx,[tbl]  
00007FF653F710C6  call        rax  

  On closer thought this is logical since the compiler must deal with 'GetData' being overwritten by a derived class. Therefore explicitly calling the base class function is needed to tell the compiler that it must use the base class:

class TableCt : public Table
{
   long GetDataCt() const
   {
      return std::stol(__super::GetData());
   }
};

 Using the keyword final could also do the trick too although it depends then on the cleverness of the compiler.

No comments:

Post a Comment

Careful with std::ranges

<ranges>   C++20 has added the the ranges library. Basically it works on ranges instead of iterators but added some subtle constraint...