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

如何在C++中通过显式链接与依赖注入动态扩展图形计算程序

实现步骤

1. 确保IShape接口可跨模块访问

首先你的app.exe中的IShape接口必须被导出,这样ellipse.dll才能继承它。修改接口头文件,添加导出宏:

// ShapeInterface.h
#ifdef SHAPE_EXPORTS
#define SHAPE_API __declspec(dllexport)
#else
#define SHAPE_API __declspec(dllimport)
#endif

class SHAPE_API IShape {
public:
    virtual ~IShape() = default;
    virtual double getArea() const = 0;
    virtual double getPerimeter() const = 0;
    virtual const char* getName() const = 0;
    // 新增销毁函数,避免跨模块内存释放问题
    virtual void destroy() { delete this; }
};

编译app.exe时,定义SHAPE_EXPORTS宏(VS中可在项目属性->C/C++->预处理器->预处理器定义中添加),编译器会生成app.lib导出库,后续ellipse.dll需要链接这个库。

2. 编写ellipse.dll代码

创建DLL项目,包含ShapeInterface.h并链接app.lib。实现Ellipse类,并导出C风格的工厂函数(避免C++名字修饰):

// Ellipse.cpp
#include "ShapeInterface.h"
#include <cmath>

class Ellipse : public IShape {
private:
    double majorAxis, minorAxis;
public:
    Ellipse(double major, double minor) : majorAxis(major), minorAxis(minor) {}

    double getArea() const override {
        return 3.1415926 * majorAxis * minorAxis;
    }

    double getPerimeter() const override {
        // 椭圆周长近似公式
        return 3.1415926 * (3*(majorAxis + minorAxis) - sqrt((3*majorAxis + minorAxis)*(majorAxis + 3*minorAxis)));
    }

    const char* getName() const override {
        return "Ellipse";
    }
};

// 导出创建椭圆的函数,统一命名为CreateShape
extern "C" SHAPE_API IShape* CreateShape(double major, double minor) {
    return new Ellipse(major, minor);
}

// 导出图形名称,供主程序识别
extern "C" SHAPE_API const char* GetShapeName() {
    return "Ellipse";
}

编译生成ellipse.dll,将其放到app.exe所在目录。

3. 修改app.exe的工厂逻辑(仅需编译一次,后续加DLL无需改动)

在现有工厂类中添加动态加载DLL的逻辑,实现遍历目录、加载DLL、注册图形创建器三个步骤:

3.1 遍历目录获取所有DLL

#include <windows.h>
#include <vector>
#include <string>

std::vector<std::wstring> GetAllDllsInCurrentDir() {
    std::vector<std::wstring> dllPaths;
    WIN32_FIND_DATAW findData;
    HANDLE hFind = FindFirstFileW(L"*.dll", &findData);
    if (hFind == INVALID_HANDLE_VALUE) return dllPaths;

    do {
        if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
            dllPaths.push_back(findData.cFileName);
        }
    } while (FindNextFileW(hFind, &findData));

    FindClose(hFind);
    return dllPaths;
}

3.2 扩展工厂类加载外部图形

#include "ShapeInterface.h"
#include <unordered_map>
#include <functional>

class ShapeFactory {
private:
    std::unordered_map<std::string, std::function<IShape*(double, double)>> _creators;
    std::vector<HMODULE> _loadedDlls; // 保存DLL句柄,退出时释放

public:
    ShapeFactory() {
        // 注册内置图形(矩形、圆形)
        RegisterBuiltinShapes();
        // 加载外部DLL中的图形
        LoadExternalShapes();
    }

    ~ShapeFactory() {
        // 释放所有加载的DLL
        for (HMODULE hDll : _loadedDlls) {
            FreeLibrary(hDll);
        }
    }

    IShape* CreateShape(const std::string& shapeName, double param1, double param2) {
        auto it = _creators.find(shapeName);
        return it != _creators.end() ? it->second(param1, param2) : nullptr;
    }

private:
    void RegisterBuiltinShapes() {
        // 示例:注册矩形(宽、高)
        _creators["Rectangle"] = [](double w, double h) {
            return new Rectangle(w, h);
        };
        // 示例:注册圆形(半径,第二个参数无用)
        _creators["Circle"] = [](double r, double) {
            return new Circle(r);
        };
    }

    void LoadExternalShapes() {
        auto dlls = GetAllDllsInCurrentDir();
        for (const auto& dllName : dlls) {
            HMODULE hDll = LoadLibraryW(dllName.c_str());
            if (!hDll) continue;

            // 获取导出的函数指针
            using GetShapeNameFunc = const char* (*)();
            using CreateShapeFunc = IShape* (*)(double, double);

            GetShapeNameFunc getShapeName = reinterpret_cast<GetShapeNameFunc>(
                GetProcAddress(hDll, "GetShapeName")
            );
            CreateShapeFunc createShape = reinterpret_cast<CreateShapeFunc>(
                GetProcAddress(hDll, "CreateShape")
            );

            if (getShapeName && createShape) {
                std::string name = getShapeName();
                _creators[name] = createShape;
                _loadedDlls.push_back(hDll); // 保存句柄防止被提前释放
            } else {
                FreeLibrary(hDll); // 不是目标DLL,释放资源
            }
        }
    }
};

3.3 使用注意

创建图形实例后,调用shape->destroy()而非直接delete,避免跨模块内存释放错误:

// 示例使用
ShapeFactory factory;
IShape* ellipse = factory.CreateShape("Ellipse", 5.0, 3.0);
if (ellipse) {
    printf("Ellipse Area: %.2f\n", ellipse->getArea());
    printf("Ellipse Perimeter: %.2f\n", ellipse->getPerimeter());
    ellipse->destroy(); // 由DLL内部销毁对象
}

关键注意事项

  • 导出库依赖:ellipse.dll必须链接app.exe生成的app.lib,否则无法解析IShape的导出符号。
  • 名字修饰:导出函数必须用extern "C",确保GetProcAddress能通过函数名找到对应符号。
  • 内存管理:统一使用接口的destroy函数销毁对象,避免跨模块堆内存操作导致的崩溃。
  • DLL放置:将ellipse.dll放到app.exe所在目录,主程序才能遍历加载。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 13:05:10