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

Posted

技术标签:

【中文标题】gcc中内联的行为:虚函数的内联,尤其是我的析构函数【英文标题】:the behavior of inline in gcc: inline of virtual function and especially destructor in my case 【发布时间】:2015-06-23 14:03:02 【问题描述】:

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

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

无论如何,这里我只是想研究一下为什么 gcc 会像现在这样。 例如,它使 .obj 非常大,但它可能有助于内联尽可能多 想要……!?我只是猜测,我并不完全理解。 (由于我的错误(我的愚蠢定义)很明显,所以我不想说 是 gcc 的错误。 ;) )

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

非常感谢您的耐心等待,我期待您的 cmets/answers。


我的程序(抱歉,还有一点额外的测试代码……;))

(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

【问题讨论】:

我不想让我的帖子太长,所以目前我还没有谈到 testVFunc()。请随意跳过。 也许这有帮助:***.com/questions/733737/… 感谢您的回复。实际上我已经阅读了那篇文章并且找不到我想要的东西 - 为什么在 TestCompiler.obj 中内联 gcc 而不是在链接/运行时选择它。同样,TestCompiler.cpp 甚至不调用 TriShape/Shape 的任何函数。 【参考方案1】:

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

【讨论】:

正如我在上面的报告中所说,我正在尝试学习 gcc 的内部结构,而不是如何避免这里的错误。为什么 gcc 会在包含 TriShape 的每个 cpp 中内联析构函数?谢谢

以上是关于gcc中内联的行为:虚函数的内联,尤其是我的析构函数的主要内容,如果未能解决你的问题,请参考以下文章

虚函数可以是内联函数吗?

多态和抽象

可以内联虚函数[重复]

C++中,子类会继承父类的虚函数表!对于父类的析构函数(虚函数) 也会继承吗?

C++ 设置基类的析构函数为虚函数

从 C++ 中的析构函数中恢复对象?