【问题标题】:Can inheritance be used to reduce the size of compiled code in C++?可以使用继承来减少 C++ 中编译代码的大小吗?
【发布时间】:2017-03-24 18:43:32
【问题描述】:

我正在开发一个基于 Arduino 的系统(即嵌入式系统)的项目,并且只有有限数量的 ROM 可供代码使用。

我发现自己需要几种不同类型的集合类(例如ListStackQueue)。 写完这些后,我注意到除了使它们表现不同的特性(例如Add vs Push vs EnqueuePop vs Dequeue)之外,它们共享许多共同的功能(例如,它们具有确切的相同的字段)。

这让我想到,如果我要创建一个包含其他类的所有通用功能的基类(我们称之为Collection)并让其他类继承自它,并让它们只实现功能不同,会导致编译代码更少吗?

我希望它会这样,因为子类都可以引用父类对公共函数的实现,但我可能是错的,所以我宁愿在尝试此更改之前先询问一下。


好的,为了进一步澄清这一点,因为人们似乎在误解,考虑这些假设的类:

class A
{
protected:
    int value;
public:
    void assign(int other)
    {
        this->value = other;
    }
}

class B : public A
{
public:
    void assignAdd5(int other)
    {
        this->value = other + 5;
    }
}

然后是代码:

A a = A();
B b = B();

a.assign(4);
b.assign(4);

我期望assign 在两种情况下都引用相同的方法(因此是相同的编译代码块),尽管类型不同,因为BA 的子类。如果是这种情况,那么让具有相似功能的类都继承自实现相似性的单个基类将产生比让每个类单独实现功能更少的编译代码,因为每个子类将使用相同的函数。

我提供的关于提示这个问题的情况的信息仅仅是背景,我认为 any 编译器是否属于这种情况,而不仅仅是我正在使用的编译器(我非常感谢答案可能是“某些编译器可能会这样做,但并非所有编译器都会这样做”,这是一个完全可以接受的答案)。

【问题讨论】:

  • 你为什么不试试看?
  • @tobi303 因为这很耗时,所以有人可能已经知道答案,而我在 SO 上找不到这个问题的答案,所以我想也许其他人可能会从答案中受益。
  • 这并不是批评,但最终数字可能会因您的具体情况而有所不同。我现在很好奇,想举个例子,它会有所作为
  • this 在某种程度上是相关的。不幸的是,接受的答案仅针对可执行文件大小无关紧要的窗口
  • 为什么有 3 种类型?一种。它支持前后推/弹。支持中间添加。当用作Queue 时,您...不要调用其他方法。仅当一种类型的权衡使得另一种类型的解决方案次优时才编写新类型,并且该成本值得支持和交付更多代码。

标签: c++ c++11 inheritance size


【解决方案1】:

我无法给出明确的答案,但我想分享我的发现。实际上,我并不像我预期的那样容易测量差异。不要把代码看得太重,我只是在尝试随机的东西,看看它是否有任何影响......

#include <iostream>
#define DERIVED
#define BASE_STUFF     int a,b,c,d,e,f,g,h,i,k,m,n,o,p,q,r,s,t,u,v,w,x,y,z; \
                       double foo1(int x){return x;}                        \
                       double foo2(int x){return x;}                        \
                       double foo3(int x){return x;}                        \
                       double foo4(int x){return x;}                        \
                       Base*  foo5(Base* x){return x;}                      \
                       FPTR   foo5(FPTR a,FPTR b,FPTR c){return a;}

typedef double (*FPTR)(int,int,double);

struct Base { BASE_STUFF };

#ifdef DERIVED
struct Derived : Base{double a0,a1,a2,a3,a4,a5,a6,a7;};
#endif

#ifndef DERIVED
struct Derived {
    double a0,a1,a2,a3,a4,a5,a6,a7;
    BASE_STUFF
};
#endif

int main(int argc, char *argv[])
{
    Base b;
    b.x = 1;
    std::cout << b.foo1(b.x);
    std::cout << b.foo5(&b)->foo2(b.a);
    Derived n;
    n.a0 = 2;
    std::cout << n.foo1(n.a0);
}

我必须再次检查数字,但令我惊讶的是,与 c 风格的 prinft 相比,使用 iostreams 时可执行文件更小。

当我用 (g++4.9.2) 编译它时

g++ main.cpp -Os -fno-exceptions -s

我得到一个 13.312 kB 的可执行文件。这与DERIVED 是否被定义无关。但是,当我用

编译它时
g++ main.cpp -Os -fno-exceptions

大小为 43.549 kB。再次独立于 DERIVED 是否被定义。

我的结论是

  • 我的例子不能很好地衡量差异
  • ...但是,尝试编译器标志会产生巨大差异,而是否继承则差别不大
  • 我不会为了减少代码大小而更改设计。编写代码,使其易于阅读、编写和使用,如果您需要从 exe 中挤出一些 kB,请使用编译器标志(另请参阅 here

【讨论】:

    【解决方案2】:

    编写一个完整的容器。

    从它私下继承。使用using 将您想要公开的方法显示出来。

    编译器最多会为 dtor/ctor 创建一些极短的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-14
      • 1970-01-01
      • 2013-03-10
      • 2015-02-24
      • 2019-04-20
      • 1970-01-01
      • 2014-01-16
      • 1970-01-01
      相关资源
      最近更新 更多