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

ARM MCU开发:链接时如何剔除未使用的C++虚函数?

问题描述

我用C++搭配arm-none-eabi-gcc开发MCU软件,通过Wrapper类适配不同实现。目前遇到的问题是:调用Wrapper的某一个函数时,ClassA的所有虚函数(哪怕没被调用)都会被编译进二进制,连带依赖的大库代码也会被包含,占用不必要的空间。

我做了最小复现示例:

Wrapper.h

#include <stdint.h>
#include "ClassA.h"

class Wrapper {
 public:
  static float square(float x);
  static float reciprocal(float x);

  static void setImplementation(ClassA *impl);

 private:
  static ClassA *classA;
};

Wrapper.cpp

#include "Wrapper.h"

ClassA *Wrapper::classA = nullptr;

float Wrapper::square(float x) { return classA->square(x); }
float Wrapper::reciprocal(float x) { return classA->reciprocal(x); }
void Wrapper::setImplementation(ClassA *impl) { classA = impl; }

ClassA.h

#include <stdint.h>

class ClassA {
 public:
  virtual ~ClassA() = default;
  virtual float square(float x);
  virtual float reciprocal(float x);
};

ClassA.cpp

#include "ClassA.h"

float ClassA::square(float x) {
  float y = x * x;
  return y;
}

float ClassA::reciprocal(float x) {
  float y = 1.0f / x;
  return y;
}

调用代码:

ClassA classA;
Wrapper::setImplementation(&classA);
static volatile float value = 0.6f;
static volatile float test = Wrapper::reciprocal(value);

编译链接命令:

arm-none-eabi-g++ -mcpu=cortex-m0plus -march=armv6-m -mthumb -mlittle-endian -mfloat-abi=soft \
                  -ggdb -Os -flto=auto -ffunction-sections -fdata-sections -fsingle-precision-constant \
                  -fno-rtti -fno-exceptions -Wno-register \
                  -Wno-pedantic -Wno-unused-parameter -Wall -Wextra -Wpedantic -Wdouble-promotion \
                  -DARM_MATH_CM0 -DCBOR_NO_HALF_FLOAT_TYPE -DDEBUG \
                  -nostartfiles --specs=nano.specs --specs=nosys.specs -Wl,--gc-sections,-no-wchar-size-warning \
                  -Wl,-e,main,-Map=output.map -T/workspaces/mspm0g1519.lds \
                  -I/workspaces/src/sandbox \
                  -o output.elf \
                  /workspaces/src/main.cpp \
                  /workspaces/src/sandbox/ClassA.cpp \
                  /workspaces/src/sandbox/Wrapper.cpp

现象:即便只调用了reciprocal,ClassA的两个函数都会被包含进二进制,正常的未使用函数剔除功能失效。

问题原因

核心原因是虚函数表的特性:

  • 当定义包含虚函数的类(比如ClassA)时,编译器会生成一张虚函数表(vtable),表中会列出所有虚函数的地址,包括未被直接调用的函数。
  • 只要实例化了ClassA(代码中ClassA classA;),或者存在指向ClassA的指针/引用并可能触发多态调用(Wrapper中使用ClassA*),链接器会认为虚函数表被引用,而虚函数表又关联了所有虚函数,因此这些函数都会被标记为“被使用”,无法被--gc-sections垃圾回收。
  • 即便只调用了其中一个虚函数,虚函数表本身需要完整存在以支持多态机制,连带所有关联的虚函数都会被保留。
解决办法

1. 拆分接口类(推荐,适配动态切换场景)

把大的ClassA拆分成多个单一职责的接口类,让Wrapper只持有需要的接口指针:

// ISquare.h
class ISquare {
public:
  virtual ~ISquare() = default;
  virtual float square(float x) = 0;
};

// IReciprocal.h
class IReciprocal {
public:
  virtual ~IReciprocal() = default;
  virtual float reciprocal(float x) = 0;
};

// ClassA同时实现两个接口
class ClassA : public ISquare, public IReciprocal {
public:
  float square(float x) override { return x*x; }
  float reciprocal(float x) override { return 1.0f/x; }
};

// 修改Wrapper,按需持有对应接口
class Wrapper {
public:
  static float reciprocal(float x) { return reciprocalImpl->reciprocal(x); }
  static void setReciprocalImpl(IReciprocal* impl) { reciprocalImpl = impl; }
private:
  static IReciprocal* reciprocalImpl;
};

这样当只使用reciprocal功能时,只有IReciprocal相关的虚函数和实现会被链接,square的代码会被垃圾回收。

2. 使用模板代替虚函数(适合编译期确定实现的场景)

如果不需要运行时动态切换实现,可以用模板Wrapper来避免虚函数表:

template<typename Impl>
class Wrapper {
public:
  static float square(float x) { return Impl::square(x); }
  static float reciprocal(float x) { return Impl::reciprocal(x); }
};

// 使用时指定实现
Wrapper<ClassA>::reciprocal(value);

编译器会在编译期直接实例化用到的函数,未使用的函数会被优化掉,完全避免虚函数表的问题。

3. 手动标记未使用虚函数为弱符号

在未使用的虚函数上添加__attribute__((weak)),让链接器在没有引用时可以剔除:

// ClassA.h
virtual float square(float x) __attribute__((weak));

但这种方法依赖编译器行为,且如果有子类重写该函数可能出问题,仅适合临时应急。

4. 避免实例化完整基类

如果只需要某一个功能,可以创建仅实现该功能的子类,而不是实例化完整的ClassA:

class ReciprocalOnly : public ClassA {
public:
  float reciprocal(float x) override { return 1.0f/x; }
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 20:19:57