You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SWIG将C++纯虚类包装为Java Director时的构造器问题

问题描述

我有一个具备Sender和Listener功能的C库,其中Listener是供客户端扩展的纯虚类,Sender采用std::shared_ptr<MyListener>接收实例以避免悬垂指针。我使用SWIG 4.3的shared_ptr特性包装智能指针类型,director特性支持在Java侧扩展MyListener类,转换成功后在Java程序中编译报错:MyListener无默认构造器,仅存在带long cPtr、boolean cMemoryOwn参数的显式构造器。即使在C的MyListener中显式声明默认构造器,生成的Java类仍无该构造器。现咨询:

  1. 如何避免生成该显式构造器并保留shared_ptr参数传递?
  2. 若无法避免,构造器的两个参数应传入什么值?
  3. 如何正确在Java中扩展该虚拟类?

相关代码

C++定义

class MyListener
{
public:
    inline virtual ~MyListener() = default;
    virtual void notify(const std::string &event) = 0;
    virtual void disposed(bool expected) = 0;
};

class MySender
{
public:
    void addListener(std::shared_ptr<MyListener> listener);
    void removeListener(std::shared_ptr<MyListener> listener);
};

SWIG接口文件

%include <std_shared_ptr.i>

%module(directors="1") MyWrapper

%{
#include "MyClasses.h"
%}

%shared_ptr(MyListener);
%feature("director") MyListener;

%include "MyClasses.h"

预期Java用法

public class Main {

    static {
        System.loadLibrary("MyWrapper");
    }
    
    public class DerivedListener extends MyListener{
        @Override
        public void notify(String event) {
            // do something
        }
    
        @Override
        public void disposed(boolean expected) {
            // do something
        }
    }

    public static void main(String[] args) {
        MySender sender = new MySender();
        DerivedListener listener = new DerivedListener();
        sender.addListener(listener);
        sender.removeListener(listener);
        return;
    }
}

生成的Java类MyListener构造器

protected MyListener(long cPtr, boolean cMemoryOwn) {
    swigCMemOwn = cMemoryOwn;
    swigCPtr = cPtr;
}
解决方案

1. 能否避免生成该显式构造器?

无法直接避免生成这个显式构造器。这是SWIG生成JNI绑定的必要结构:director类需要通过底层C对象的指针完成Java与C的方法转发,即使在C中为MyListener添加默认构造器,SWIG也不会为Java类生成默认构造器——因为C纯虚类本身无法直接实例化,SWIG会强制Java类通过JNI绑定的方式创建对应实例。

2. 构造器参数的含义与手动调用风险

绝对不要手动调用这个构造器,它是SWIG内部用于绑定C++对象的接口。如果强行传入参数:

  • cPtr是底层C++MyListener实例的内存地址
  • cMemoryOwn表示Java侧是否拥有该对象的内存管理权

手动传入参数极易导致内存泄漏、野指针等严重问题,完全不推荐这么做。

3. 正确在Java中扩展虚拟类的方法

利用SWIG的director机制自动处理底层对象创建,只需调整SWIG接口和Java代码:

步骤1:修改SWIG接口,添加默认构造器包装

在SWIG接口文件中,为MyListener添加%extend块,让SWIG生成可供Java调用的默认构造器:

%include <std_shared_ptr.i>

%module(directors="1") MyWrapper

%{
#include "MyClasses.h"
%}

%shared_ptr(MyListener);
%feature("director") MyListener;

// 添加这部分代码,生成Java侧的默认构造器
%extend MyListener {
    MyListener() {
        // SWIG会自动生成director代理类,无需手动实例化纯虚类
        return new $self();
    }
}

%include "MyClasses.h"

步骤2:调整Java代码

将内部类改为静态内部类(或独立类),确保可以在静态main方法中实例化,并显式调用父类构造器:

public class Main {
    static {
        System.loadLibrary("MyWrapper");
    }

    // 改为静态内部类,或提取为独立类
    public static class DerivedListener extends MyListener {
        public DerivedListener() {
            // 调用SWIG生成的默认构造器,自动创建C++侧的director代理对象
            super();
        }

        @Override
        public void notify(String event) {
            System.out.println("Received event: " + event);
        }

        @Override
        public void disposed(boolean expected) {
            System.out.println("Disposed, expected: " + expected);
        }
    }

    public static void main(String[] args) {
        MySender sender = new MySender();
        DerivedListener listener = new DerivedListener();
        sender.addListener(listener);
        sender.removeListener(listener);
    }
}

原理说明

SWIG的director机制会在Java类继承MyListener时,自动创建一个C代理类,这个代理类会将C侧的回调转发给Java实现。%shared_ptr(MyListener)会让SWIG自动完成Java对象到std::shared_ptr<MyListener>的转换,无需手动处理智能指针的传递逻辑。

内容的提问来源于stack exchange,提问作者Evan Altair

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 00:17:15