Object Oriented Programming In Good Ol’ C¶
C Is Simple, So Why C++?¶
struct
to aggregate related things into a new typeCopy supported by compiler
Functions to add functionality
Pointers to pass things by reference (e.g. for modifying functionality)
C struct initialization
If used ⟶ good
If unused ⟶ uninitialized
struct point
: Something That Looks Like A Point¶
Initialization can be done
But need not!
#include <gtest/gtest.h>
struct point
{
int x;
int y;
};
TEST(point_c_suite, struct_initialization)
{
struct point p1; // <--- uninitialized (there be dragons!)
struct point p2 = {1,2}; // <--- good ol' C "struct initialization"
ASSERT_EQ(p2.x, 1);
ASSERT_EQ(p2.y, 2);
(void)p1; // avoid "unused" warning
}
Good Style: Explicit Initialization Functions¶
C++ constructor initializes an object
Object referred to as
this
(part of C++ syntax)In C, everything is explicit
⟶ pointer to object (usually given as first parameter)
Default Constructor
#include <gtest/gtest.h>
struct point
{
int x;
int y;
};
void point_init_default(struct point* self) // <--- "constructor" in C
{
self->x = 0;
self->y = 0;
}
TEST(point_c_suite, c_default_constructor_init)
{
struct point p; // <--- uninitialized
point_init_default(&p); // <--- post-birth initialization
ASSERT_EQ(p.x, 0);
ASSERT_EQ(p.y, 0);
}
Alternative …
Something that does not let things uninitialized
Expensive though if objects are larger (return by copy)
#include <gtest/gtest.h>
struct point
{
int x;
int y;
};
struct point point_create_default(void) // <--- "constructor" in C
{
struct point p;
p.x = 0;
p.y = 0;
return p;
}
TEST(point_c_suite, c_default_constructor_create)
{
struct point p = point_create_default(); // <--- uninitialized state not possible
ASSERT_EQ(p.x, 0);
ASSERT_EQ(p.y, 0);
}
Constructor: initialize a struct point
with its coordinates
#include <gtest/gtest.h>
struct point
{
int x;
int y;
};
struct point point_create(int x, int y)
{
struct point p = {x,y};
return p;
}
TEST(point_c_suite, c_constructor)
{
struct point p = point_create(1,2);
ASSERT_EQ(p.x, 1);
ASSERT_EQ(p.y, 2);
}
Comparison Functions: (In)Equality¶
Compare two points
Parameters (
lhs
andrhs
) passed by copy (astruct point
is 8 bytes in size which is the size of a pointer)A rather clumsy way to express
==
and!=
#include <gtest/gtest.h>
struct point
{
int x;
int y;
};
int /*bool*/ point_equal(point lhs, point rhs)
{
return
lhs.x == rhs.x &&
lhs.y == rhs.y;
}
int /*bool*/ point_not_equal(point lhs, point rhs)
{
return ! point_equal(lhs, rhs);
}
TEST(point_c_suite, eq_ne)
{
const struct point p = {1,2};
const struct point q = {3,4};
int b;
b = point_equal(p, q);
ASSERT_FALSE(b);
b = point_equal(p, p);
ASSERT_TRUE(b);
b = point_not_equal(p, p);
ASSERT_FALSE(b);
b = point_not_equal(p, q);
ASSERT_TRUE(b);
}
Moving Points¶
Move operation modifies object
⟶ passed as (non-const) pointer
A rather clumsy way to express
+=
#include <gtest/gtest.h>
struct point
{
int x;
int y;
};
void point_move(point* self, int x, int y)
{
self->x += x;
self->y += y;
}
TEST(point_c_suite, move)
{
point p = {1,2};
point_move(&p, 3, 4);
ASSERT_EQ(p.x, 4);
ASSERT_EQ(p.y, 6);
}
Vector Addition and Subtraction¶
Creates a new point out of two points which are left unmodified
Pass by copy (again because size of
struct point
is rather small)Return by copy
#include <gtest/gtest.h>
struct point
{
int x;
int y;
};
point point_add(point lhs, point rhs)
{
struct point sum;
sum.x = lhs.x + rhs.x;
sum.y = lhs.y + rhs.y;
return sum;
}
point point_sub(point lhs, point rhs)
{
struct point diff;
diff.x = lhs.x - rhs.x;
diff.y = lhs.y - rhs.y;
return diff;
}
TEST(point_c_suite, add_sub)
{
const point p = {1,2};
const point vec = {3,4};
const point sum = point_add(p, vec);
ASSERT_EQ(sum.x, 4);
ASSERT_EQ(sum.y, 6);
const point diff = point_sub(p, vec);
ASSERT_EQ(diff.x, -2);
ASSERT_EQ(diff.y, -2);
}
Distance Of Two Points¶
Points passed by copy
#include <gtest/gtest.h>
#include <math.h>
struct point
{
int x;
int y;
};
double point_distance(point lhs, point rhs)
{
int a = rhs.x - lhs.x;
int b = rhs.y - lhs.y;
return sqrt(a*a + b*b);
}
TEST(point_c_suite, distance)
{
const point p1 = {3,4};
const point p2 = {5,7};
double dst = point_distance(p1, p2);
ASSERT_FLOAT_EQ(dst, sqrt(13));
}
Length Of A Point’s Position Vector¶
Computation does not modify the object
⟶ pass by
const
pointer
#include <gtest/gtest.h>
#include <math.h>
struct point
{
int x;
int y;
};
double point_abs(const point* self)
{
int hyp = self->x * self->x + self->y * self->y;
return sqrt(hyp);
}
TEST(point_c_suite, abs)
{
const point p = {3,4};
double abs = point_abs(&p);
ASSERT_FLOAT_EQ(abs, 5.0);
}
Summary: Is C Good Enough?¶
Members are public
struct point p = {42, 7}; ... p.x = 666; // unwanted at times
⟶ Bugs are only a matter of time
Counter argument: “Real programmers don’t write bugs”
Function just hang around
point_add()
is the addition operator which belongs tostruct point
Just nobody knows
Inconsistent parameter passing schema: how is the object passed?
double point_abs(const point* self); point point_sub(point lhs, point rhs);
Clean/consistent initialization wanted
Constructor (and destructor)
Error checking
Overloaded operators - e.g. addition of two
struct point
objects using the addition operator+
Methods on Objects, like moving a point:
p.abs();