[blog] C++ for C programmers #2

Returning user-defined types

In C, suppose we want to create a function that gets the position of a certain device. We usually would have created a Position type definition, and the functions to retrieve this position would look like one of these:

Position* get_position(); // function 1
void get_position(Position* this_position); // function 2

In function 1 the called function would allocate the memory for the Position object and return the address to the caller function. In function 2, the caller would allocate this memory and pass the object address to the function. In C++ you are allowed to return user defined types directly from functions.

struct Position {
   float x;
   float y;
};
Position get_position() {
//code
}
int foo() {
 auto p = get_position();
// code..
}

Generic Programming With Templates

The mind behind Generic Programming is Stepanov. Long story short, C++ takes code reuse to another level with templates:

#include <iostream>

template <class T>
void swap(T& x, T& y) 
{
	T temp = x;
	x = y;
	y = temp;
}

int main(void)
{
	int a = 6;
	int b = 10;
	float f = 3.7f;
	float g = 4.3f;
	double d = 3.14159265358979323846;
	double e = 6.00008573217894365218;
	swap(a, b);
	std::cout << "a is now " << a << "; b is now " << b << std::endl;
	swap(f, g);
	std::cout << "f is now " << f << "; g is now " << g << std::endl;
	swap(d, e);
	std::cout << "d is now " << d << "; e is now " << e << std::endl;
	return 0;
}

Output:

a is now 10; b is now 6
f is now 4.3; g is now 3.7
d is now 6.00009; e is now 3.14159

Object Life Cycle

In C, the storage duration of an object depends on how you declare them in your code. C++ works with constructors and destructors for the user-defined types, the classes. Classes are like structs that can have functions, loosely speaking.

An object’s constructor is called just after its storage duration begins and the destructor is called just before it ends. They have no return type and the same name as the struct. The destructor has a ~ to the beginning of the class name.

The compiler makes sure the constructor and destructor are invoked automatically for objects with static, local, and thread local storage duration. For objects with dynamic storage duration, you use the keywords new and delete instead of malloc and free.

#include <cstdio>

struct Car {
	Car(const float engine_arg) : engine(engine_arg) { // constructor
		printf("I am a car with a %.2f engine\n\r", engine);
	}
	~Car() { //destructor
		printf("I was a car with a %.2f engine\n\r", engine);
	}
	const float engine;
};

void local_car_30(void) {
	Car car_local{ 3.0 }; //memory allocated, constructor called
	return; //destructor called, memory deallocated
}
int main() {
	Car* car1 = new Car{2.0};  //memory allocated, constructor called
	local_car_30(); 
	auto car2 = new Car{1.0}; 
	delete car1; //destructor called, memory deallocated
	delete car2; 
}

The output:

I am a car with a 2.00 engine
I am a car with a 3.00 engine
I was a car with a 3.00 engine
I am a car with a 1.00 engine
I was a car with a 2.00 engine
I was a car with a 1.00 engine
There is a concept in C++ programming called "RAII" which means "resource allocation is initialization", sometimes also called "constructor acquires, destructor releases".
Initializing data in C++ is a mess. For C programmers, you must get the differences from initializing Fully Featured Classes, structs that have data members and methods from Plain-Old Data structures - pure data containers you already know. 

Smart Pointers

A raw pointer is a memory address, just that. And you have to take care of the memory management. The idea of smart pointers is to wrap dynamic objects so the compiler will take care of the memory management.

On the list above, suppose I had not used the delete keyword for car1 and car2 – which I have allocated dynamically (with the new keyword). The destructors would never be called and that would mean a memory leak. The use of a smart pointer, in this case the unique_pointer assures memory is cleaned up.

#include <cstdio>
#include <memory>
struct Car {
    Car(const float engine_arg) : engine(engine_arg) { // constructor
        printf("I am a car with a %.2f engine\n\r", engine);
    }
    ~Car() { //destructor
        printf("I was a car with a %.2f engine\n\r", engine);
    }
    const float engine;
};
void create_cars(void) {
    std::unique_ptr<Car> car1{ new Car{2.0} };
    auto car2 = new Car{ 1.0 }; 
    Car car3{ 3.0 };
} //no delete for car2. memory leak!
int main() {
    create_cars();  
} 

Output:

I am a car with a 2.00 engine
I am a car with a 1.00 engine
I am a car with a 3.00 engine
I was a car with a 3.00 engine
I was a car with a 2.00 engine

Note the destructor of car2 was never called.

Author: Antonio Giacomelli de Oliveira

Embedded Systems Engineer

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: