简体   繁体   中英

Can inheritance be used to reduce the size of compiled code in C++?

I am working on a project for an Arduino-based sytem (ie an embedded system) and have a limited amount of ROM for code to live in.

I have found myself needing several different types of collection classes (eg List , Stack , Queue ). After writing these I noticed that aside from the features that make them behave differently (eg Add vs Push vs Enqueue , Pop vs Dequeue ) they share a lot of common functionality (eg they have the exact same fields).

This got me thinking, if I were to create a base class that contained all of the common functionality of the other classes (let's call it Collection ) and made the other classes inherit from it, and have them implement only the functionality that differs, would that result in less compiled code?

I'm expecting that it will because the child classes can all refer to the parent's implementations of the common functions, but I could be wrong so I'd rather ask before I attempt this change.


Ok, to clarify this further since people seem to be misinterpreting, consider these hypothetical classes:

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;
    }
}

And then the code:

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

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

I am expecting that assign refers to the same method in both cases (and thus the same compiled block of code) despite the different types because B is a subclass of A . If this is the case then having classes with similar functionality all inherit from a single base class implementing the similarities would produce less compiled code than making each class implement the functionality separately because each child class would be using the same function.

The information I provided regarding the situation that prompted this question was merely background, I am in whether this is the case with any compiler, not just the one I'm using (and I fully appreciate that the answer may be "it's possible that some compilers do this, but not all of them do", and that's a perfectly acceptable answer).

I cannot give a definite answer, but I want to share my findings. Actually I isnt that easy to measure the difference as I expected. Dont take the code too serious, I was just trying random stuff to see if it has any impact...

#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);
}

I have to check again for the numbers, but to my surprise the executable was smaller when using iostream s as compared to c-style prinft .

When I compile this with (g++4.9.2)

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

I get an executable of 13.312 kB. And that is independent of DERIVED being defined or not. However, when I compile it with

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

the size is 43.549 kB. Again independent of DERIVED being defined or not.

My conclusions are

  • my example isnt good to measure the difference
  • ...however, experimenting with compiler flags results in huge differences, while inheritance or not makes little difference
  • I would not change the design with the aim to reduce the code size. Write your code so that it is easy to read, write, and use and use compiler flags if you need to squeeze some kB out of your exe (see also here )

Write a monolithic container that does it all.

Inherit from it privately. Use using to bring methods you want to expose into view.

At most some extremely short methods will be created for dtor/ctor by the compiler.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM