首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

C++ DLL 導出類完整指南 - Windows 與 Linux 跨平台封裝實踐

     編輯:C++ 入門

Windows 平台:__declspec(dllexport/dllimport) 機制

問題背景

在 DLL 專案中定義類,需導出供外部調用。直接在類前加 __declspec(dllexport) 編譯 DLL 時正確,但引用該頭文件的客戶端編譯會報錯 (因客戶端需 dllimport)。

標準解法:宏切換導出/導入

在頭文件中定義宏,根據編譯環境自動切換:

// MyClass.h
#ifndef MYCLASS_H
#define MYCLASS_H

#ifdef MYCLASS_EXPORTS  // DLL 專案定義此宏
#define MYCLASS_API __declspec(dllexport)
#else                     // 客戶端專案不定義
#define MYCLASS_API __declspec(dllimport)
#endif

class MYCLASS_API MyClass {
public:
    MyClass();
    void doSomething();
private:
    int data_;
};

#endif // MYCLASS_H

在 DLL 專案的屬性 → C/C++ → Preprocessor → Preprocessor Definitions 中添加 MYCLASS_EXPORTS。客戶端只需包含頭文件、鏈接 .lib,無需修改代碼。

.def 文件替代方案

使用模塊定義文件 (MyClass.def) 列出導出符號,避免修改頭文件:

LIBRARY MyClass
EXPORTS
    ?doSomething@MyClass@@QEAAXXZ  // 裝飾名,需用 dumpbin 獲取
    ??0MyClass@@QEAA@XZ            // 構造函數
    ??1MyClass@@QEAA@XZ            // 析構函數

但需手動維護裝飾名,不推薦用於類導出。

Linux 平台:共享庫 導出類

Linux 下無 dllexport,默認所有符號可見。但為規範接口、隱藏內部符號,建議:

  1. 對外接口函數用 extern "C" 防止名字修飾,便于 dlsym 查找。
  2. 類本身不直接導出,而是提供工廠函數返回基類指針。

插件式設計範例

// SMSGamePlugin.h (公共基類)
class CSMSGamePlugin {
public:
    virtual int Initialize() = 0;
    virtual int Load() = 0;
    virtual int Handle() = 0;
    virtual ~CSMSGamePlugin() {}
};

// Flower.h (插件實現)
#include "SMSGamePlugin.h"
extern "C" CSMSGamePlugin* GetGameObject(); // 工廠函數

class CFlower : public CSMSGamePlugin {
public:
    int Initialize() override;
    int Load() override;
    int Handle() override;
};

// Flower.cpp
#include "Flower.h"
extern "C" CSMSGamePlugin* GetGameObject() { return new CFlower(); }
int CFlower::Initialize() { puts("Init"); return 0; }
int CFlower::Load() { puts("Load"); return 0; }
int CFlower::Handle() { puts("Handle"); return 0; }

主程序動態加載

#include <dlfcn.h>
#include "SMSGamePlugin.h"

int main() {
    void* handle = dlopen("./Flower.so", RTLD_LAZY);
    auto create = (CSMSGamePlugin*(*)())dlsym(handle, "GetGameObject");
    CSMSGamePlugin* plugin = create();
    plugin->Initialize();
    plugin->Load();
    plugin->Handle();
    delete plugin;
    dlclose(handle);
    return 0;
}

編譯:g++ -fPIC -shared -o Flower.so Flower.cpp,主程序 g++ -rdynamic -ldl -o main main.cpp。

關鍵注意事項

  • 對象生命週期:使用 dlopen 獲取的對象指針,在 dlclose 前必須 delete,否則因代碼段卸載導致段錯誤。
  • C++ ABI 兼容:跨編譯器/版本傳遞 C++ 對象指針不安全,建議用 C 接口 (工廠函數 + 虛函數表) 隔離。
  • Windows 導出類成員函數:宏方式會導出所有 public 成員,若需隱藏實現細節,可用 Pimpl 惯用法 (指針指向實現結構體)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved