Hillel
Hillel

Reputation: 331

C++ Polymorphism/virtual function not working here

I have two type of classes Ll2TxPacketBds and Ll2TxLbPacketBds the latter class inherits from the prior class defined as follows:

class Ll2TxPacketBds
{
public:
    Ll2TxPacketBds(MainCorePfDrv *pCorePfDrv, const MainEthTxPacketDesc &pktDesc, uint32 pktProd);
    Ll2TxPacketBds(Ll2TxPacketBds&& moveFrom);  //Move Constructor
    ~Ll2TxPacketBds();

    virtual core_tx_bd_union bd(uint8 nSge, uint32 pktProd);
    uint8 sglSize() const;

protected:
    MainEthTxPacketDesc m_pktDesc;
    vector<shared_ptr<DrvBuf>> m_vectDrvBuf;
    uint8 m_txDst; // diffrent bd for loop back packet
};

class Ll2TxLbPacketBds : public Ll2TxPacketBds
{
public:
    Ll2TxLbPacketBds(MainCorePfDrv *pCorePfDrv, const MainEthTxPacketDesc &pktDesc, uint32 pktProd) : Ll2TxPacketBds(pCorePfDrv, pktDesc, pktProd) {};
    Ll2TxLbPacketBds(Ll2TxLbPacketBds&& moveFrom) : Ll2TxPacketBds((Ll2TxPacketBds&&)moveFrom) {};
    ~Ll2TxLbPacketBds() { Ll2TxPacketBds::~Ll2TxPacketBds(); };

    core_tx_bd_union bd(uint8 nSge, uint32 pktProd);
};

as you can see the son class reimplemets virtual bd method. There is a dequeue of father pointer type defined as followed:

deque<Ll2TxPacketBds*> m_txPacketList;

Using two different ways of pushing element into deque as followed:

1.

Ll2TxPacketBds* bd = (m_txDest != CORE_TX_DEST_LB) ? &Ll2TxPacketBds(m_pCorePfDrv, *pPktDesc, m_pktProd) : &Ll2TxLbPacketBds(m_pCorePfDrv, *pPktDesc, m_pktProd);
    m_txPacketList.push_back(bd); // Polymorphism/virtual function not working here

2.

 if (m_txDest != CORE_TX_DEST_LB)
    {
        Ll2TxPacketBds* bd = &Ll2TxPacketBds(m_pCorePfDrv, *pPktDesc, m_pktProd);
        m_txPacketList.push_back(bd);

    }
    else
    {
        Ll2TxLbPacketBds* bdLb = &Ll2TxLbPacketBds(m_pCorePfDrv, *pPktDesc, m_pktProd);
        m_txPacketList.push_back(bdLb); //Works here
    }

After elements are popped and bd() is called as followed:

m_pBdRing->peekProd() = m_txPacketList.back()->bd(nBd, m_pktProd); 

Using debugger I do see that in both #1 and #2 the correct constructor is called...

If the popped element was created by method #1 Polymorphism will not happen and Ll2TxPacketBds implementation of bd(..) will always be called while method #2 works as expected as far as Polymorphism goes.

Upvotes: 1

Views: 896

Answers (1)

Resurrection
Resurrection

Reputation: 4106

You cannot take base pointer of a stack allocated object because it gets destroyed and you are left only with the pointer to the base part of object that no longer exists. So it is in fact undefined behaviour because the object does not exist anymore and it is just luck that it works (sometimes) which is exactly how UB usually manifests.

The solution is to replace:

Ll2TxPacketBds* bd = &Ll2TxPacketBds(m_pCorePfDrv, *pPktDesc, m_pktProd);

with

Ll2TxPacketBds* bd = new Ll2TxPacketBds(m_pCorePfDrv, *pPktDesc, m_pktProd);

And then deallocate the stuff you saved in the container. For that you will NEED to make destructor virtual as well!

You can make it into smart pointers which will save you the deallocation but you will still need virtual destructor (even if empty in the base class).

Upvotes: 7

Related Questions