从napi导出的C++类如何在ArkTS侧被派生
========================

#### 问题现象

从napi导出的C++类，支持在ArkTS侧被派生吗?

如果支持的话，如何在ArkTS侧派生使用从napi导出的C++类，并提供示例Demo。

#### 解决方案

napi导出的C++类，支持被派生。

在index.d.ts中，已经提供了ts层的c++接口，所以需要将ArkTS需要使用的C++类，在index.d.ts中声明，才能转为ArkTS侧的父类。

**方法一：使用[AKI](https://gitcode.com/openharmony-sig/aki)的JSBIND\_CLASS接口将C++类绑定到JavaScript环境，再在ArkTS侧用extends继承****：**

1.  C++侧：akiusepractice.cpp中定义使用的类结构。
    
    #include <aki/jsbind.h>
    #include <hilog/log.h>
    #undef LOG\_DOMAIN
    #undef LOG\_TAG
    #define LOG\_DOMAIN 0x3200  _// 全局domain宏，标识业务领域_
    #define LOG\_TAG "MY\_TAG" _  // 全局tag宏，标识模块日志tag_
    
    _// 类/结构体_
    struct Person {
        Person(int mage, std::string mname, double mweight) : age(mage), name(mname), weight(mweight)
        {
            OH\_LOG\_INFO(LOG\_APP, "AkiInfo: Person: age %{public}d", mage);
        }
    
        std::string sayHello()
        {
            OH\_LOG\_INFO(LOG\_APP, "AkiInfo: SayHello");
            return "hello from Native";
        }
        int age;
        std::string name;
        double weight;
    };
    
    _// 全局函数_
    Person MakePerson()
    {
        Person person(91, "aki", 128.8);
        return person;
    }
    
    _// Aki JSBind语法糖_
    JSBIND\_GLOBAL()
    {
        JSBIND\_FUNCTION(MakePerson);
    }
    
    JSBIND\_CLASS(Person)
    {
        JSBIND\_CONSTRUCTOR<int, std::string, double>();
        JSBIND\_METHOD(sayHello);
        JSBIND\_PROPERTY(age);
        JSBIND\_PROPERTY(name);
        JSBIND\_PROPERTY(weight);
    }
    
    _// TODO：知识点：使用JSBIND\_ADDON注册OpenHarmony Native插件，可从JavaScript import导入插件。注册AKI插件名:即为编译\*.so名称，规则与NAPI一致。这里注册AKI插件名为:akiusepractice_
    JSBIND\_ADDON(akiusepractice)
    
2.  桥接层：index.d.ts中声明使用的C++类。
    
    export class Person {
      constructor(age:number, name:string, weight:number);
      sayHello: () => string;
      age: number;
      name: string;
      weight: number;
    }
    
    export const MakePerson: () => Person;
    
3.  在指定路径下（如：项目根路径/entry），输入如下命令安装ohpm har包依赖，该命令会自动将AKI依赖添加到build-profile.json5中。
    
    cd entry
    ohpm install @ohos/aki
    
4.  在CMakeLists.txt中链接AKI库。
    
    _\# 设置AKI根路径_
    set(AKI\_ROOT\_PATH ${CMAKE\_CURRENT\_SOURCE\_DIR}/../../../oh\_modules/@ohos/aki)
    _\# 将CMAKE\_MODULE\_PATH变量的值设置为AKI\_ROOT\_PATH变量的值，这样CMake在查找自定义模块时会查看这个路径。_
    set(CMAKE\_MODULE\_PATH ${AKI\_ROOT\_PATH})
    _\# 用于查找并加载名为"Aki"的库_
    find\_package(Aki REQUIRED)
    _\# 创建并编译一个akiusepractice库_
    add\_library(akiusepractice SHARED pluginPlan.cpp)
    _\# 将Aki::libjsbind库链接到akiusepractice_
    target\_link\_libraries(akiusepractice PUBLIC Aki::libjsbind libhilog\_ndk.z.so)
    
5.  ArkTS侧：index.ets中使用派生类。
    
    import { hilog } from '@kit.PerformanceAnalysisKit';
    import testNapi from 'libentry.so';
    import libAki from 'libakiusepractice.so'; _// 导入自定义AKI插件_
    
    const DOMAIN = 0x0000;
    
    class Man extends libAki.Person {
      sayWord() {
        console.info('AkiInfo ArkTS Hello! Hello!');
      }
    }
    
    @Entry
    @Component
    struct Index {
      @State message: string = 'Hello World';
    
      build() {
        Row() {
          Column() {
            Button(this.message)
              .fontSize($r('app.float.page\_text\_font\_size'))
              .fontWeight(FontWeight.Bold)
              .onClick(() => {
                this.message = 'Welcome';
                hilog.info(DOMAIN, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3));
              })
            Button("aki")
              .onClick(()=>{
                let man = new Man(99, "aki", 128.8);
                man.sayWord();
                hilog.info(DOMAIN, 'testTag', 'AkiInfo Get: %{public}s', man.sayHello());
                libAki.MakePerson();
              })
          }
          .width('100%')
        }
        .height('100%')
      }
    }
    

*   **方案二****：****通过napi\_define\_class建立ArkTS类与C++侧的映射关系，并将对应的对象挂载到export上导出，后用extends继承。**
    1.  C++侧：napi\_init.cpp中定义使用的类结构。
        
        _/\*_
         _\* Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved._
         _\*/_
        #include "napi/native\_api.h"
        #include <hilog/log.h>
        #include <string>
        
        _// 定义C++的类_
        class MyDemo {
        public:
            MyDemo(){};
            explicit MyDemo(std::string name);
            std::string m\_name;
            double Add(double a, double b);
            double Sub(double a, double b);
        };
        
        MyDemo::MyDemo(std::string name)
        {
            this->m\_name = name;
        }
        
        double MyDemo::Add(double a, double b)
        {
            return a + b + 1000;
        }
        
        double MyDemo::Sub(double a, double b)
        {
            return a - b + 1000;
        }
        
        _// ArkTS对象构造器_
        static napi\_value JsConstructor(napi\_env env, napi\_callback\_info info)
        {
           _ // 创建Napi对象_
            napi\_value jDemo = nullptr;
            size\_t argc = 0;
            napi\_value args\[1\] = {0};
         _   // 获取构造函数的入参_
            napi\_get\_cb\_info(env, info, &argc, args, &jDemo, nullptr);
          _  // 参数通过args\[0\]获取_
            char name\[50\];
            size\_t result = 0;
            napi\_get\_value\_string\_utf8(env, args\[0\], name, sizeof(name) + 1, &result);
           _ // 创建C对象_
            MyDemo \*cDemo = new MyDemo(name);
            OH\_LOG\_INFO(LOG\_APP, "%{public}s", (cDemo->m\_name).c\_str());
           _ // 设置JS对象name属性_
            napi\_set\_named\_property(env, jDemo, "name", args\[0\]);
          _  // 将JS对象与C++对象绑定_
            napi\_wrap(
                env, jDemo, cDemo,
              _  // 定义回调函数，用于回收JS对象，用于销毁C++对象并防止内存泄漏_
                \[\](napi\_env env, void \*finalize\_data, void \*finalize\_hint) {
                    MyDemo \*cDemo = (MyDemo \*)finalize\_data;
                    delete cDemo;
                    cDemo = nullptr;
                },
                nullptr, nullptr);
            return jDemo;
        }
        
        _// ArkTS对象add函数_
        static napi\_value JsAdd(napi\_env env, napi\_callback\_info info)
        {
            size\_t argc = 2;
            napi\_value args\[2\] = {nullptr};
            napi\_value jDemo = nullptr;
            napi\_get\_cb\_info(env, info, &argc, args, &jDemo, nullptr);
            MyDemo \*cDemo = nullptr;
           _ // 将ArkTS对象转换为C对象_
            napi\_unwrap(env, jDemo, (void \*\*)&cDemo);
           _ // 获取ArkTS传递的参数_
            int value0;
            napi\_get\_value\_int32(env, args\[0\], &value0);
            int value1;
            napi\_get\_value\_int32(env, args\[1\], &value1);
            int cResult = cDemo->Add(value0, value1);
            napi\_value jResult;
            napi\_create\_int32(env, cResult, &jResult);
            return jResult;
        }
        
        _// ArkTS对象sub函数_
        static napi\_value JsSub(napi\_env env, napi\_callback\_info info)
        {
            size\_t argc = 2;
            napi\_value args\[2\] = {nullptr};
            napi\_value jDemo = nullptr;
            napi\_get\_cb\_info(env, info, &argc, args, &jDemo, nullptr);
            MyDemo \*cDemo = nullptr;
            _// 将ArkTS对象转换为C对象_
            napi\_unwrap(env, jDemo, (void \*\*)&cDemo);
           _ // 获取ArkTS传递的参数_
            int value0;
            napi\_get\_value\_int32(env, args\[0\], &value0);
            int value1;
            napi\_get\_value\_int32(env, args\[1\], &value1);
            int cResult = cDemo->Sub(value0, value1);
            napi\_value jResult;
            napi\_create\_int32(env, cResult, &jResult);
            return jResult;
        }
        
        static napi\_value Add(napi\_env env, napi\_callback\_info info)
        {
            size\_t requireArgc = 2;
            size\_t argc = 2;
            napi\_value args\[2\] = {nullptr};
            napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);
            napi\_valuetype valuetype0;
            napi\_typeof(env, args\[0\], &valuetype0);
            napi\_valuetype valuetype1;
            napi\_typeof(env, args\[1\], &valuetype1);
            int value0;
            napi\_get\_value\_int32(env, args\[0\], &value0);
            int value1;
            napi\_get\_value\_int32(env, args\[1\], &value1);
            MyDemo \*demo = new MyDemo();
           _ // 调用类的成员函数_
            int result = demo->Add(value0, value1);
            napi\_value sum;
            napi\_create\_int32(env, result, &sum);
            delete demo;
            return sum;
        }
        
        static napi\_value Sub(napi\_env env, napi\_callback\_info info)
        {
            size\_t argc = 2;
            napi\_value args\[2\] = {nullptr};
            napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);
            napi\_valuetype valuetype0;
            napi\_typeof(env, args\[0\], &valuetype0);
            napi\_valuetype valuetype1;
            napi\_typeof(env, args\[1\], &valuetype1);
            int value0;
            napi\_get\_value\_int32(env, args\[0\], &value0);
            int value1;
            napi\_get\_value\_int32(env, args\[1\], &value1);
            MyDemo \*demo = new MyDemo();
          _  // 调用类的成员函数_
            int result = demo->Sub(value0, value1);
            napi\_value num;
            napi\_create\_int32(env, result, &num);
            delete demo;
            return num;
        }
        
        EXTERN\_C\_START
        static napi\_value Init(napi\_env env, napi\_value exports)
        {
            napi\_property\_descriptor desc\[\] = {
                { "add", nullptr, Add, nullptr, nullptr, nullptr, napi\_default, nullptr },
                {"sub", nullptr, Sub, nullptr, nullptr, nullptr, napi\_default, nullptr}
            };
            napi\_define\_properties(env, exports, sizeof(desc) / sizeof(desc\[0\]), desc);
        
          _  // 通过napi\_fine\_class建立ArkTS类和C++端的映射关系，然后将相应的对象挂载到export上_
            napi\_property\_descriptor classProp\[\] = {{"add", nullptr, JsAdd, nullptr, nullptr, nullptr, napi\_default, nullptr},
                                                    {"sub", nullptr, JsSub, nullptr, nullptr, nullptr, napi\_default, nullptr}};
            napi\_value jDemo = nullptr;
            const char \*jDemoName = "MyDemo";
           _ // 建立ArkTS构造函数与C++方法之间的关联_
            napi\_define\_class(env, jDemoName, sizeof(jDemoName), JsConstructor, nullptr,
                              sizeof(classProp) / sizeof(classProp\[0\]), classProp, &jDemo);
            napi\_set\_named\_property(env, exports, jDemoName, jDemo);
            return exports;
        }
        EXTERN\_C\_END
        
        static napi\_module demoModule = {
            .nm\_version = 1,
            .nm\_flags = 0,
            .nm\_filename = nullptr,
            .nm\_register\_func = Init,
            .nm\_modname = "entry",
            .nm\_priv = ((void\*)0),
            .reserved = { 0 },
        };
        
        extern "C" \_\_attribute\_\_((constructor)) void RegisterEntryModule(void)
        {
            napi\_module\_register(&demoModule);
        }
        
    2.  桥接层：index.d.ts中声明使用的C++类。
        
        declare namespace testNapi {
          const add: (a: number, b: number) => number;
          const sub: (a: number, b: number) => number;
          _// 定义ArkTS接口_
          class MyDemo {
            constructor(name:string)
            name: string
            add(a: number, b: number): number
            sub(a: number, b: number): number
          }
        }
        export default testNapi;
        
    3.  ArkTS侧：index.ets中使用派生类。
        
        import { hilog } from '@kit.PerformanceAnalysisKit';
        import testNapi from 'libentry.so';
        
        class Employee extends testNapi.MyDemo { _// 继承C++的类_
          salary: number = 0;
          calculateTaxes(): number {
            return this.salary \* 0.42;
          }
        }
        
        @Entry
        @Component
        struct Index {
        
          build() {
            Row() {
              Column() {
                Text('Hello World')
                  .fontSize($r('app.float.page\_text\_font\_size'))
                  .fontWeight(FontWeight.Bold)
                  .onClick(() => {
                    let em = new Employee('abc');
                    hilog.info(0x0000, 'testTag', 'Employee 1000 + 8 + 9 = %{public}d', em.add(8,9));
                    hilog.info(0x0000, 'testTag', 'Employee 1000 + 10 - 3 = %{public}d', em.sub(10, 3));
                  })
              }
              .width('100%')
            }
            .height('100%')
          }
        }
        

#### 总结

napi导出的C++类，支持被派生。