Avoiding new/delete
Consider the following:
1
2
3
4
5
6
7
8
9
10
11
12
13 | class Thing {
public:
int i;
Thing(int In) {
this->i = In;
printf("Hey\n");
}
};
int main(void) {
Thing* t = new Thing(1);
return 0;
}
|
This is an example of what I consider unfortunate OOP: by thinking of Thing as its own conceptual entity, we have introduced allocating the memory on the heap rather than the stack. The downside of this is that new/delete requires the operating system to do work in dealing out virtual memory to the object, which can be bad for performance.
Much the same damage can be done in C without OOP:
| struct Thing {
int i;
};
int main(void) {
Thing* t = malloc(sizeof(Thing));
t->i = 1;
return 0;
}
|
The way to avoid virtual memory is to not declare 't' as a pointer:
This way it will be allocated on the stack, which is usually better and definitely better for small data bundles. It also has the advantage of the 'constructor' being voluntary: you can declare it as
which will not require the compiler to generate code assigning values to fields upfront, so that you can mete it out only when necessary.
Inheritance
Inheritance in C++ looks something like this:
| class Parent {
public:
int i;
char a;
};
class Child : public Parent {
public:
double d[3];
};
|
The traditional counterpart to inheritance is composition, which goes something like this in C:
| struct Parent {
int i;
char a;
};
struct Child {
Parent parent;
double d[3];
};
|
The problem with this is that it can become verbose when we want to assign values to a Child:
| Child c;
c.parent.i = 1;
c.parent.a = 'a';
c.d[0] = -1.0;
c.d[1] = 3.14;
c.d[2] = 0.0;
|
As you can see, when we want to access 'int i' and 'char a' we have to first access the Child's 'parent' field. If we keep composing structs like this, the problem compounds and it becomes tedious to write code.
My solution is to mostly avoid struct composition beyond one level, and to keep the names short. One way to add something that kinda looks like inheritance using macros is:
| #define Parent \
int i;\
char a
struct Child {
Parent;
double d[3];
};
|
Now we have the option of code reuse as well as immediate access to the fields.