为什么SWIG会损坏成员类的内容?

2024-10-02 18:24:11 发布

您现在位置:Python中文网/ 问答频道 /正文

当一个类包含指向另一个类的指针时,SWIG似乎会不恰当地报告另一个类的内容。以下是最小的可重复性示例(SSCCE):

配置h:

class Config
{
    int debug;      
public:
    void showDebug(void);
};

class ConfigContainer
{
    Config *config; 
public:
    ConfigContainer(Config *);
    void showDebug(void);
};

在配置.cpp公司名称:

^{pr2}$

现在,当我用SWIG翻译成Python时,我得到了:

^{3}$

当我在C++中运行这个序列时,报告的内容是相同的。但是对于SWIG,即使地址是相同的,SWIG似乎破坏了地址中的值。在

响应者说,这是因为一旦调用showDebug(),ConfigContainer的Config *config成员在Python中的引用计数减少了。在

如何告诉SWIG,告诉Python让config成员保持独立?在


Tags: config示例内容地址报告成员publicclass
2条回答

这看起来是一个终生的问题。在

如果传递给函数的对象将被保存在某个地方,则需要通过递增引用计数器来通知Python运行时。在

如果不这样做,那么对象可能在第一次调用终止后被销毁,因此config指针实际上指向Python认为可重用的内存。在

您所观察到的是内存实际上已被其他对象重用(因此您可以看到内容的变化)。在

在cpython中,使用引用计数器管理对象生命周期(使用单独的收集算法来解决引用循环的问题)。每个对象都有一个计数器,显示有多少其他对象指向它,每次使用该计数器时都必须保持正确。在

如果你将一个对象传递给一个函数,而函数代码将指向该对象的指针存储在某个地方,那么它也应该增加指向对象的引用计数器。如果它未能通知对象新存储的引用,问题是对象可能会被销毁,留下一个指向未分配(或稍后重用)内存的悬空指针。在

相反的问题(增加引用太多)是内存泄漏,因为如果引用计数器大于零,即使没有人使用它,Python内存管理器也不会回收对象内存。在

请参阅http://docs.python.org/2/c-api/refcounting.html和相关文档。在

当Python创建您的Config对象时,它不保存对它的永久引用。您的配置容器类只包含一个简单的指针,所以即使在C++中,如果不保持对象活着,配置容器也不会知道。在

下面的代码行使用Python创建一个临时的Config对象,该对象在该行完成时被销毁:

c = ConfigContainer(Config())

如果向构造函数和析构函数添加封装外形,可以看到这一点,就像我下面所做的那样:

^{pr2}$

所以现在ConfigContainer持有一个被破坏的指针。简单的解决方案是在完成之前保留对Config的引用。在

>>> import x
>>> c = x.Config()
__cdecl Config::Config(void)
>>> cc = x.ConfigContainer(c)
__cdecl ConfigContainer::ConfigContainer(class Config *)

复杂的解决方案是实现引用计数(参见6.25 C++ reference counted objects - ref/unref feature)。在

另一个解决方案是通过更改容器来使用SWIG对std::shared_ptr的支持:

class ConfigContainer
{
    std::shared_ptr<Config> config; 
public:
    ConfigContainer(std::shared_ptr<Config>&);
    ~ConfigContainer();
    void showDebug(void);
};

以下文件是必需的接口:

%include <std_shared_ptr.i>
%shared_ptr(Config)          // This instantiates the template for SWIG

现在,Config将被引用计数:

>>> import x
>>> cc = x.ConfigContainer(x.Config())
__cdecl Config::Config(void)
__cdecl ConfigContainer::ConfigContainer(class std::shared_ptr<class Config> &)

ConfigContainer现在包含对Config的引用。销毁容器会销毁对Config的最后一个引用:

>>> del cc
__cdecl ConfigContainer::~ConfigContainer(void)
__cdecl Config::~Config(void)

但是如果Python有它自己的引用,那么当Python处理完它时,只会销毁容器:

>>> c = x.Config()
__cdecl Config::Config(void)
>>> cc = x.ConfigContainer(c)
__cdecl ConfigContainer::ConfigContainer(class std::shared_ptr<class Config> &)
>>> del cc
__cdecl ConfigContainer::~ConfigContainer(void)
>>> del c
__cdecl Config::~Config(void)

相关问题 更多 >