内联在gcc中的行为:虚拟函数的内联,尤其是在我的例子中的析构函数

the behavior of inline in gcc: inline of virtual function and especially destructor in my case

本文关键字:我的 尤其是 析构函数 gcc 虚拟 函数      更新时间:2023-10-16

这里棘手的部分是BUILD_DEBUG_class_MEMBER的定义在TestCompiler.cpp中,我知道我可以简单地不定义它来解决错误(请参阅下面运行时结果中的断言)

我的问题是:
1.在我的例子中,为什么在TestCompiler.obj文件中内联虚拟内联函数即使我不调用delete或TriShape/Shape的任何类方法
2.为什么在运行时调用TestCompiler.o的内联版本但不是Class.o中的其他版本。(不仅下面的rumtime结果,gdb也显示了它。)

无论如何,在这里我只是想研究为什么gcc会像现在这样做。例如,它使.obj非常大,但它可能有助于尽可能多地内联想要…!?我只是猜测,我不完全理解。(因为我的错误(我愚蠢的定义)是显而易见的,所以我宁愿不说出来是gcc的bug。))

顺便说一句,我测试过的编译器是gcc 4.4.7,也是VS2013仅限前者的原因断言。而且,原始代码库在一个不是我的库中所以我无法轻易更改Shape文件。(ClassA是一种工厂,Shape类是库的核心。)

非常感谢你的耐心,我期待着你的评论/回答。


我的程序(很抱歉,剩下一些额外的测试代码…;)

(ClassA是一种工厂,Shape类是源于库的核心。)

//================================================================//
// TestCompiler.cpp
//
//#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
#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
#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)
    {
        printf("Shape ctor: this:%p  size:%d n", this, sizeof(*this));
    }
    //TODO: inline it! :P
    virtual ~Shape()
    {
        //m_ptr
        printf("Shape dtor: this:%p  size:%d n", this, sizeof(*this));
    }
    inline virtual int testVFunc()
    {
        return -1;
    }
    Shape* m_ptr;
};

//test ONLY:
//#include "TriShape.h"
//================================================================//
//TriShape.h
#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;
}

运行时输出

$ ./inlineTest
TC: main start
Shape ctor: this:0x995b018 size:8
TriShape ctor: this:0x995b018 size:8
TriShape dtor AFTER class: this:0x995b018 size:16
List dtor: this:0x995b020 size:8 dummy:0x20fe1
inlineTest: TriShape.h:22: virtual MyList::~MyList(): Assertion
`m_dummy==0xABCD' failed.
Aborted (core dumped)

-S生成的可读取对象代码,以显示它在BOTH.asm中内联

In TestCompiler.asm:
The destructor is inlned up this section
.section .text._ZN8TriShapeD1Ev,"axG",@progbits,_ZN8TriShapeD1Ev,comdat
(...)
call _ZN6MyListD1Ev
(...)
call _ZN5ShapeD2Ev
In ClassA.asm:
The destructor is inlned up this section
.section .text._ZN8TriShapeD1Ev,"axG",@progbits,_ZN8TriShapeD1Ev,comdat
(...)
call _ZN5ShapeD2Ev

在链接器生成的映射文件中(TriShape的dtor周围的grep结果)

.text._ZN8TriShapeD1Ev
0x0000000000000000 0x0 ClassA.o
.text._ZN8TriShapeD0Ev
0x0000000000000000 0x0 ClassA.o        //oops, ALL ZEROS means something??
(...)
.rel.text._ZN8TriShapeD1Ev
0x0000000000000000 0x0 /usr/lib/../lib/crt1.o
.rel.text._ZN8TriShapeD0Ev
0x0000000000000000 0x0 /usr/lib/../lib/crt1.o
(...)
.text._ZN8TriShapeD1Ev
0x0000000008048a80 0xb9 TestCompiler.o
0x0000000008048a80 _ZN8TriShapeD1Ev
*fill* 0x0000000008048b39 0x1 90909090
.text._ZN8TriShapeD0Ev
0x0000000008048b3a 0xb9 TestCompiler.o
0x0000000008048b3a _ZN8TriShapeD0Ev
*fill* 0x0000000008048bf3 0x1 90909090

不幸的是,您违反了TriShape的一个定义规则,因此试图猜测编译器为什么会做一些特定的事情不太可能产生有用的信息。编译器可以假设类和函数在所有源文件中都是相同的,因此它可以选择一个并在所有地方执行该代码。