In this short presentation struct inheritance and virtual member function in pure C are demonstrated. With these fundamental techniques, more advanced design patterns are unlocked.
2. Outline
● Implement the following Object-Oriented techniques in
Pure C:
○ Struct “inheritance”
○ Member functions using member variables/functions
○ “Virtual” functions
● PS:
○ I’m not going to condemn non-OO like C, but provide more
programming techniques in C. We may base on the task situations to
consider its fitness.
4. Class Inheritance in C++ and C (1/2)
This is how we do class inheritance in C++:
// A and B are derived from Base
class Base
{
public:
int type;
int op_Base(int i);
};
class A : public Base
{
public:
int* dataA;
int op_A(int i);
};
class B : public Base
{
public:
int* dataB;
int op_B(int i);
};
This is how we do struct “inheritance” in C:
struct Base
{
int type;
int (*op_Base) (struct Base* _this, int i);
};
struct A
{
struct Base _base; // inherit from Base
int* dataA;
int (*op_A) (struct A* _this, int i);
};
struct B
{
struct Base _base; //inherit from Base
int* dataB;
int (*op_B) (struct B* _this, int i);
};
PS: I would tell you why we need _this later
5. Class Inheritance in C++ and C (1/2)
In C++, for derived class A, we can access
members of both Base and A:
Given we have:
A a;
Then we can use:
a.dataA
a.op_A(5);
a.type
a.op_Base(5);
We can do the same thing in C as we do in
C++:
Given we have:
struct A a;
Then we can use:
a.dataA
a.op_A(&a, 5);
and
a._base.type
a._base.op_Base(&a._base, 5);
or, more elegantly
struct Base* pBase = (struct Base*)&a;
pBase->type
pBase->op_Base(pBase, 5);
PS: in this way, struct A users can access Base
members as they are struct Base users.
6. Memory Allocation of Struct “Inheritance”
AddrOffset 0~3: type
AddrOffset 4~7: op_Base
AddrOffset 8~11: dataA
AddrOffset 12~15: op_A
...
start address of a
(i.e., &a)
if we declare struct A a;
The memory allocation of a is like:
a._base
a
Remark:
start address of a == &a == &a._base == &a._base.type
a._base.type == ((struct Base*)&a)->type //this concept can be used in virtual
8. Member Function Using Other Class
Members (C++ vs. C)
In C++, member functions can access other
class members (functions, variables):
For example, we can define function op_A as
below:
int A::op_A(int i)
{
return op_Base(i) + type + dataA[i];
}
We can do the same thing in C as we do in C++:
(using _this)
int A_op_A(struct A* _this, int i)
{
return _this->_base.op_Base(&_this->_base, i)
+ _this->_base.type
+ _this->dataA[i];
}
For struct construction, we have:
void A_ctor(struct A* _this)
{
Base_ctor(&_this->_base); // call Base ctor
_this->op_A = A_op_A;
...
}
For A’s user, we call:
a.op_A(&a, 5);
10. Virtual Functions in C++
Why we need virtual?
● What is virtual function?
○ By Wikipedia’s definition: In object-oriented programming, a virtual function
or virtual method is a function or method whose behavior can be overridden
within an inheriting class by a function with the same signature. This concept is
an important part of the polymorphism portion of object-oriented programming
(OOP)
● Advantages: (better software architecture design)
1. base class users can use derived classes without modifying their
code.
2. we can localize the knowledge of which derived class is being used.
11. Virtual Functions in C++
In C++, virtual function goes like this:
class Interface {
...
virtual bool execute();
}
class ImpA : public Interface {
…
virtual bool execute();
}
class ImpADerived : public ImpA {
...
virtual bool execute();
}
For general class Interface users:
void func(Interface* pInterf) { pInterf->execute(); }
ImpADerived a; func(&a);
PS: actually, ImpADerived::execute() is called, instead of Interface::execute()
PS: Inside func(), it doesn’t need to know which version of execute() is being
used.
Class Userhold
Actually,ituses...
12. “Virtual” Functions in Pure C (1/2)
In C, virtual functions can be achieved via pointer manipulation:
struct Interface {
...
BOOL (*execute) (struct Interface* _this);
}
struct ImpA {
struct Interface _base; //derived from Interface
...
}
struct ImpADerived {
struct ImpA _base; //derived from ImpA
…
}
in ImpADerived constructor:
void ImpADerived_ctor (struct ImpADerived* _this) {
_this->_base->_base.execute = ImpADerived_execute;
…
}
continued in the next page
Class Userhold
Actually,ituse...
13. “Virtual” Functions in Pure C (2/2)
In C, virtual functions can be achieved via pointer manipulation:
The following is how we implement ImpADerived’s execute():
BOOL ImpADerived_execute (struct Interface* _this) {
// here we can check _this->mType to confirm struct version first
struct ImpADerived* _this2 = (struct ImpADerived*)_this;
_this2->doMoreThings(_this2);
…
}
So just like what we did in C++ virtual, for general Interface users:
void func(struct Interface* pInterf) { pInterf->execute(pInterf); }
struct ImpADerived a;
ImpADerived_ctor(&a);
func((struct Interface*)&a);
Inside func(), ImpADerived::execute() is used instead of Interface::execute()
Now we achieve the same functionality in C as in C++
Class Userhold
Actually,ituses...