Экстремальный Cи. Параллелизм, ООП и продвинутые возможности [2021] Амини Камран

BEST OF THE BEST

Читал эту книгу, когда устроился в Huawei на C-программиста. Книга — одна из лучших, если нужно глубже разобраться в C и понять процесс сборки и линковки. Мне понравился детальный разбор, как исходный код превращается в исполняемый файл. Затронуты объектные файлы (.o), статические библиотеки (.a) и динамические библиотеки (.so). Никогда раньше не встречал такого подробного объяснения.

Автор показывает, как реализовать основы ООП на C:

  • Инкапсуляция: Здесь мы учимся создавать собственные конструкторы, деструкторы и методы “классов”.
  • Композиция и агрегация: Объясняется, как создавать классы, которые владеют или используют другие классы.
  • Наследование: Автор сводит наследование к композиции и предлагает два варианта реализации. Также приводится пример, как реализовать множественное наследование.
  • Полиморфизм: Классная часть, где показывается, как своими руками создать таблицу виртуальных функций.

В книге представлены основы UNIX/Linux: системные вызовы, ядро, процессы, синхронизация и многопоточность. Заканчивается книга обзором вспомогательного ПО: фреймворки для тестирования, системы сборки, отладка.

Приведу пример кода с “ООП” на С. Создадим кошку, которая зверь. Все звери умеют издавать звук и знают свое имя.

animal_private.h - связующий заголовок для базового класса и наследников. В нем лежит наш виртуальный метод “звук”

typedef void (*MakeSound_t)();

typedef struct Animal{
    char* name;
    MakeSound_t makeSound;
} Animal_t;

animal.h - интерфейс зверя

struct Animal;

struct Animal* AnimalNew();
void AnimalCtor(struct Animal* obj, const char* name);
void AnimalDtor(struct Animal* obj);

void AnimalMakeSound(struct Animal* obj);
const char* AnimalGetName(struct Animal* obj);

animal.c - пишем конструкторы, деструкторы и методы: звук, имя. Методы будут иметь реализацию по умолчанию

#include "animal_private.h"

Animal_t* AnimalNew() {
    return malloc(sizeof(Animal_t));
}

static void MakeSound() {
    printf("No sound\n");
}

void AnimalCtor(Animal_t* obj, const char* name) {
    obj->name = malloc(strlen(name) + 1);
    strcpy(obj->name, name);
    obj->makeSound = MakeSound;
}

void AnimalDtor(Animal_t* obj) {
    if (obj) {
        free(obj->name);
    }
}

void AnimalMakeSound(Animal_t* obj) {
    obj->makeSound();
}

const char* AnimalGetName(Animal_t* obj) {
    return obj->name;
}

cat.h - интерфейс кошки. В нем нет мяуканья и имени, потому что оно есть у зверя уже

struct Cat;

struct Cat* CatNew();
void CatCtor(struct Cat* obj, const char* name);
void CatDtor(struct Cat* obj);

int CatLivesLeft(struct Cat* obj);

cat.c - реализуем свои методы и переопределяем только звук

#include "animal_private.h"
#include "animal.h"

typedef struct {
    Animal_t base;
    int lives;
} Cat;

static void MakeSoundCat() {
    printf("Meow\n");
}

struct Cat* CatNew() {
    return malloc(sizeof(Cat));
}

void CatCtor(Cat* obj, const char* name){
    AnimalCtor((Animal_t*)obj, name);
    obj->lives = 9;
    obj->base.makeSound = MakeSoundCat;
}

void CatDtor(Cat* obj){
    AnimalDtor((Animal_t*)obj);
}

int CatLivesLeft(Cat* obj) {
    return obj->lives;
}

main.c

#include "animal.h"
#include "cat.h"

int main(int argc, char** argv) {
  struct Animal* animal = AnimalNew();
  AnimalCtor(animal, "Unknown");
  printf("Name: %s\n", AnimalGetName(animal)); // Name Unknown
  AnimalMakeSound(animal); // No sound
  AnimalDtor(animal);
  free(animal);

  struct Cat* cat = CatNew();
  CatCtor(cat, "Circle");
  printf("Name: %s\n", AnimalGetName((struct Animal*)cat)); // Name Circle
  AnimalMakeSound((struct Animal*)cat); // Meow
  printf("Cat have %d lives\n", CatLivesLeft(cat)); // Cat have 9 lives
  CatDtor(cat);
  free(cat);

  return 0;
}