1

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.

3
  • This code seems obfuscated. Commented May 16, 2016 at 9:15
  • 4
    You are pushing the address of temporary objects onto your queue. When you exit the block where the push is done you have dangling pointers and UB. Commented May 16, 2016 at 9:21
  • it isnt slicing since It redefines a virtual method but doesn't add new members or methods Commented May 16, 2016 at 9:21

1 Answer 1

7

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).

Sign up to request clarification or add additional context in comments.

2 Comments

even if the push and pop all happen in the same function ?
@Hillel In your code it does not happen in the same block and that is a problem. If it was in the same block it should work as expected although it would be pretty odd design. The object is destroyed when the block (not function) ends.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.