Fred Hui
Fred Hui

Reputation: 31

why the destructor of a class is "inlined" in a cpp which which ONLY includes the class's header file

By dumping the object code generated (TestCompiler.o and TestA.o), I have found that the code of the destructor of TriShape is generated in BOTH the .o file above.

Even though TestCompiler.cpp DOES NOT try to use the object of the class TriShape, why the destructor is "inlined" in the .o file? What is the benefit for the compiler in doing so??

BTW, the compiler I have tested is gcc 4.4.7 (linux), clang-600.0.54 (OSX) and also VS2013. The former two shows the same result which I have mentioned above.

Thank you for your suggestions and advice in advance!

Here is my program:

//================================================================//

TestCompiler.cpp - which, by chance, includes TriShape.h but doesn't even use it

//#include "stdafx.h"
#include <stdio.h>
#include "TestA.h"


#define BUILD_DEBUG_CLASS_MEMBER    // :)
#include "TriShape.h"           // include it just for testing compiler/linker in our case 


int main( int argc, char* argv[] )
{
    printf( "TC: main start \n" );

    //TestA::gTestAFunc();
    gTestAFunc();

    // calls to TriShape::testVFunc, etc
    //...

    printf( "TC: main finish \n" );
    return 0;
}

//================================================================//

TestA.h

#pragma once

extern void gTestAFunc();

//================================================================//

TestA.cpp

#include <stdio.h>
#include "ClassA.h"

void gTestAFunc()
{
    ClassA* pA = new ClassA();
    pA->createS();
    pA->removeS();
    delete pA;
}

//================================================================//

ClassA.h

#pragma once

#include "Shape.h"


class ClassA
{
public:
    ClassA()
        : m_pShape( NULL )
    {
    }

    void createS();

    void removeS();

    Shape* m_pShape;
};

//================================================================//

ClassA.cpp - which includes TriShape.h

#include <stdio.h>
#include "ClassA.h"

//#define BUILD_DEBUG_CLASS_MEMBER  // don't define it :)
#include "TriShape.h"


void ClassA::createS()
{
    m_pShape = new TriShape;
}

void ClassA::removeS()
{
    delete m_pShape;
    m_pShape = NULL;
}

//================================================================//

Shape.h

#pragma once

class Shape //:: MemoryObject
{
public:
    Shape()
        : m_ptr( NULL )
    {
    }

    virtual ~Shape()
    {
    }

    inline virtual int testVFunc()
    {
        return -1;
    }

    Shape* m_ptr;
};


//================================================================//

TriShape.h - destructor declared itself to be inline

#pragma once

#include <assert.h>
#include "Shape.h"

#define FIX_NUM 0xABCD


class MyList
{
public:
    MyList()
        : m_dummy( FIX_NUM )
    {

    }

    //TODO: inline it! :P
    virtual ~MyList()
    {
        printf( "List dtor: this:%p  size:%d  dummy:0x%x \n", this, sizeof( *this ), m_dummy );
        assert( m_dummy == FIX_NUM );
        //#pragma message( "List dtor here" )
    }

    int m_dummy;
};

class TriShape : public Shape
{
public:
    TriShape()
        //: m_debugMember()
    {
        printf( "TriShape ctor: this:%p  size:%d \n", this, sizeof( *this ) );
    }


#if 1
    //caseT1
    virtual ~TriShape();
#else
    //caseT2
    virtual ~TriShape()
    {
        printf( "TriShape dtor IN class: this:%p  size:%d \n", this, sizeof( *this ) );
#pragma message( "TriShape dtor here IN class" )
    }
#endif

    virtual int testVFunc();

#ifdef BUILD_DEBUG_CLASS_MEMBER
    MyList m_debugMember;
#endif
};


// inline dtor
#if 1
inline TriShape::~TriShape()
{
    printf( "TriShape dtor AFTER class: this:%p  size:%d \n", this, sizeof( *this ) );

#pragma message( "TriShape dtor here AFTER class" )
#ifdef BUILD_DEBUG_CLASS_MEMBER
#pragma message("\tit is defined: BUILD_DEBUG_CLASS_MEMBER")
#endif
}
#endif

// inline virutal func
inline int TriShape::testVFunc()
{
    printf( "TriShape testVFunc AFTER class: this:%p  size:%d \n", this, sizeof( *this ) );
#pragma message( "TriShape testVFunc here AFTER class" )
#ifdef BUILD_DEBUG_CLASS_MEMBER
#pragma message("\tit is defined: BUILD_DEBUG_CLASS_MEMBER")
#endif
    return 0;
}

Upvotes: 0

Views: 83

Answers (2)

MSalters
MSalters

Reputation: 179809

The C++ compiles one file at a time, logically. When it compiles testCompiler.cpp, it has no way to know whether other files will also contain a definition of a destructor. Therefore, the compiler has to be pessimistic and compile it anyway. Only the linker discovers that there are multiple (non-conflicting, legal) definitions.

The root cause for this is that C++ follows the C model of compilation. In a cleaner model, the first stage parser would find all the functions, the linker would then determine what it needed, and then the third stage compiler would compile the functions as needed. This is how modern languages work. It's also a lot faster because the stages can overlap in time and parallelize better.

Upvotes: 1

Serge Ballesta
Serge Ballesta

Reputation: 148900

You describe the proper symptom, but give a wrong diagnostic: the destructor was not inlined.

What happens for the compiler? It compiles TestCompiler.cpp that includes TriShape.h. After the pre-processing, the definition of TriShape::~TriShape is present, so the compiler generates code for it.

If you do not want this code to be duplicated in different modules, put it in its own cpp file and link it with the other compilation units.

Upvotes: 1

Related Questions