C++中是否可以使用继承来减小编译后代码的大小?

3
我正在为基于Arduino的系统(即嵌入式系统)开发项目,我只有有限的ROM用于代码存储。
我发现自己需要几种不同类型的集合类(例如List、Stack、Queue)。在编写这些代码后,我注意到除了使它们不同的一些特性之外(例如Add vs Push vs Enqueue, Pop 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);

我认为无论哪种编译器,只要BA的子类,assign应该在两种情况下都指向同一方法(因此相同的编译代码)。因此,让拥有类似功能的类们都继承自一个实现了这些相似性的基类,比让每个类分别实现这些功能所需的编译代码更少,因为每个子类将使用相同的函数。
我提供的关于引发这个问题的情况的信息仅仅是背景资料,我关心的是是否在任何编译器中都是这种情况 (不仅限于我正在使用的编译器),我完全理解答案可能是 "某些编译器可能会这样做,但并非所有编译器都会这样做",这是一个完全可以接受的答案。

1
为什么不试一试看呢? - 463035818_is_not_a_number
2
这并不是批评,但对于您的特定情况,最终数字可能会有所不同。我现在很好奇,正在尝试找到一个例子,看看它是否有所不同。 - 463035818_is_not_a_number
这与编程有关。不幸的是,被接受的答案只涉及Windows,其中可执行文件的大小并不太重要。 - 463035818_is_not_a_number
1
为什么要有三种类型呢?只需要一种类型。它支持推/弹出后面和前面的元素,也支持在中间添加元素。当用作“队列”时,您不需要调用其他方法。只有当某个类型的权衡使得另一种类型的解决方案变得次优,并且这种成本值得支持和发布更多代码时,才编写新类型。 - Yakk - Adam Nevraumont
你使用的是哪个集成开发环境(IDE)? - wonko realtime
显示剩余10条评论
2个回答

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风格的要小。
当我使用(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时使用编译器标志(也请参见此处

0
编写一个一体化容器,它可以完成所有任务。
私有继承它。使用using将要公开的方法带入视野。
最多只会由编译器创建一些极短的dtor/ctor方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接