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
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.