3

在检查内存泄漏后,我在我的应用程序中遇到了这个问题,并发现我的一些类根本没有被破坏。

下面的代码分为 3 个文件,它应该实现一个名为pimpl的模式。预期的情况是让Cimpl构造函数和析构函数都打印它们的消息。然而,这不是我用 g++ 得到的。在我的应用程序中,只有构造函数被调用。

类.h:

#include <memory>

class Cimpl;

class Cpimpl {
    std::auto_ptr<Cimpl> impl;
public:
    Cpimpl();
};

类.cpp:

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

class Cimpl {
public:
    Cimpl() {
        printf("Cimpl::Cimpl()\n");
    }
    ~Cimpl() {
        printf("Cimpl::~Cimpl()\n");
    }
};    

Cpimpl::Cpimpl() {
    this->impl.reset(new Cimpl);
}

主.cpp:

#include "classes.h"

int main() {
    Cpimpl c;
    return 0;
}

这是我能够进一步发现的:

g++ -Wall -c main.cpp
g++ -Wall -c classes.cpp
g++ -Wall main.o classes.o -o app_bug
g++ -Wall classes.o main.o -o app_ok

看起来在两种可能的情况之一中调用了析构函数,这取决于链接顺序。使用 app_ok 我能够得到正确的场景,而 app_bug 的行为与我的应用程序完全一样。

在这种情况下,我是否缺少一点智慧?感谢您提前提出任何建议!

4

4 回答 4

1

pimpl 习惯用法的目标是不必在头文件中公开实现类的定义。但是所有标准智能指针都需要在声明时对其模板参数的定义可见,才能正常工作。

这意味着这是您真正想要使用new,delete和裸指针的少数情况之一。(如果我对此有误,并且有一个可用于 pimpl 的标准智能指针,请告诉我。)

类.h

struct Cimpl;

struct Cpimpl
{
    Cpimpl();
    ~Cpimpl();

    // other public methods here

private:
    Cimpl *ptr;

    // Cpimpl must be uncopyable or else make these copy the Cimpl
    Cpimpl(const Cpimpl&);
    Cpimpl& operator=(const Cpimpl&);
};

类.cpp

#include <stdio.h>

struct Cimpl
{
    Cimpl()
    {
        puts("Cimpl::Cimpl()");
    }
    ~Cimpl()
    {
        puts("Cimpl::~Cimpl()");
    }

    // etc
};

Cpimpl::Cpimpl() : ptr(new Cimpl) {}
Cpimpl::~Cpimpl() { delete ptr; }

// etc
于 2012-09-07T18:46:23.753 回答
1

问题是在 auto_ptr<Cimpl>对象的定义点,Cimpl是一个不完整的类型,也就是说,编译器只看到了一个前向声明Cimpl。没关系,但由于它最终会删除它持有指针的对象,因此您必须遵守此要求,从 [expr.delete]/5 开始:

如果要删除的对象在删除点具有不完整的类类型,并且完整的类具有非平凡的析构函数或释放函数,则行为未定义。

所以这段代码遇到了未定义的行为,所有的赌注都被取消了。

于 2012-09-07T18:52:09.387 回答
0

该代码违反了单一定义规则。在 classes.h 中有一个类的定义,在文件 classes.cpp 中Cimpl有一个不同的类定义。Cimpl结果是未定义的行为。对一个类有多个定义是可以的,但它们必须相同。

于 2012-09-07T18:14:27.987 回答
0

为清楚起见进行了编辑,原始保留在下面。

此代码具有未定义的行为,因为在main.cpp隐式Cpimpl::~Cpimpl析构函数的上下文中只有 的前向声明Cimpl,但auto_ptr(或任何其他形式的做delete)需要完整定义才能合法地清理Cimpl. 鉴于这是未定义的行为,您的观察结果无需进一步解释。

原答案:

我怀疑这里发生的事情是隐式析构函数Cpimpl是在 的上下文中生成的,classes.h并且无法访问Cimpl. 然后,当auto_ptr尝试做它的事情并清理其包含的指针时,它会删除一个不完整的类,这是未定义的行为。鉴于它是未定义的,我们不必进一步解释它完全可以接受,它可以根据链接顺序以不同的方式工作。

我怀疑Cpimpl在源文件中带有定义的显式析构函数可以解决您的问题。

编辑:实际上现在我再看一遍,我相信你的程序违反了一个定义规则。在main.cpp其中看到一个不知道如何调用析构函数的隐式析构函数Cimpl(因为它只有一个前向声明)。在classes.cpp隐式析构函数中确实可以访问Cimpl' 的定义,因此可以调用它的析构函数。

于 2012-09-07T18:14:48.000 回答