Introdução
Migrar de C++ para Zig é uma transformação significativa. C++ é uma linguagem complexa com herança, templates, exceções, RAII via destrutores, e uma Standard Template Library rica. Zig substitui toda essa complexidade por um conjunto menor de primitivas mais poderosas.
Este guia cobre a conversão dos padrões mais comuns de C++ para Zig. Para comparação detalhada, veja Zig vs C++.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja Como Instalar Zig
- Conhecimento de C++
- Familiaridade básica com Zig. Consulte Introdução ao Zig
Classes para Structs
C++
class Vetor3D {
private:
double x_, y_, z_;
public:
Vetor3D(double x, double y, double z) : x_(x), y_(y), z_(z) {}
double magnitude() const {
return std::sqrt(x_*x_ + y_*y_ + z_*z_);
}
Vetor3D operator+(const Vetor3D& outro) const {
return Vetor3D(x_ + outro.x_, y_ + outro.y_, z_ + outro.z_);
}
};
Zig
const std = @import("std");
const Vetor3D = struct {
x: f64,
y: f64,
z: f64,
pub fn criar(x: f64, y: f64, z: f64) Vetor3D {
return .{ .x = x, .y = y, .z = z };
}
pub fn magnitude(self: Vetor3D) f64 {
return std.math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z);
}
pub fn somar(self: Vetor3D, outro: Vetor3D) Vetor3D {
return .{
.x = self.x + outro.x,
.y = self.y + outro.y,
.z = self.z + outro.z,
};
}
};
Em Zig, não há sobrecarga de operadores. Use métodos com nomes descritivos. Não há construtores — use funções estáticas como criar() ou init().
Templates para Comptime
C++
template<typename T>
class Pilha {
std::vector<T> items;
public:
void push(const T& item) { items.push_back(item); }
T pop() {
T item = items.back();
items.pop_back();
return item;
}
bool empty() const { return items.empty(); }
};
// Uso
Pilha<int> pilha;
pilha.push(42);
Zig
fn Pilha(comptime T: type) type {
return struct {
items: std.ArrayList(T),
const Self = @This();
pub fn init(allocator: std.mem.Allocator) Self {
return .{ .items = std.ArrayList(T).init(allocator) };
}
pub fn deinit(self: *Self) void {
self.items.deinit();
}
pub fn push(self: *Self, item: T) !void {
try self.items.append(item);
}
pub fn pop(self: *Self) ?T {
return self.items.popOrNull();
}
pub fn empty(self: Self) bool {
return self.items.items.len == 0;
}
};
}
// Uso
var pilha = Pilha(i32).init(allocator);
defer pilha.deinit();
try pilha.push(42);
RAII e Destrutores para defer
C++
class Arquivo {
FILE* handle;
public:
Arquivo(const char* path) : handle(fopen(path, "r")) {
if (!handle) throw std::runtime_error("Erro ao abrir");
}
~Arquivo() {
if (handle) fclose(handle);
}
// Move-only
Arquivo(Arquivo&& other) noexcept : handle(other.handle) {
other.handle = nullptr;
}
};
void processar() {
Arquivo f("dados.txt"); // destrutor chamado automaticamente
}
Zig
const Arquivo = struct {
handle: std.fs.File,
pub fn abrir(caminho: []const u8) !Arquivo {
return .{
.handle = try std.fs.cwd().openFile(caminho, .{}),
};
}
pub fn fechar(self: *Arquivo) void {
self.handle.close();
}
};
fn processar() !void {
var f = try Arquivo.abrir("dados.txt");
defer f.fechar(); // equivalente ao destrutor
}
Em Zig, defer substitui RAII. É explícito — o programador decide quando e o que é liberado. errdefer executa apenas em caso de erro. Veja Padrões Errdefer.
Exceções para Error Unions
C++
double dividir(double a, double b) {
if (b == 0) throw std::invalid_argument("Divisão por zero");
return a / b;
}
try {
double resultado = dividir(10, 0);
} catch (const std::invalid_argument& e) {
std::cerr << e.what() << std::endl;
}
Zig
fn dividir(a: f64, b: f64) !f64 {
if (b == 0) return error.DivisaoPorZero;
return a / b;
}
const resultado = dividir(10, 0) catch |err| {
std.debug.print("Erro: {}\n", .{err});
return;
};
Veja Error Sets Customizados e Error Logging.
Smart Pointers para Allocators
C++
auto ptr = std::make_unique<MinhaClasse>(args...);
auto shared = std::make_shared<MinhaClasse>(args...);
std::weak_ptr<MinhaClasse> weak = shared;
Zig
// unique_ptr equivalente
const ptr = try allocator.create(MinhaStruct);
defer allocator.destroy(ptr);
// Não há shared_ptr nativo — implementar se necessário
// ou usar arena allocator para gerenciamento em grupo
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
Zig não tem smart pointers. O gerenciamento é via allocators e defer. Para compartilhamento, use arena allocators ou implemente contagem de referências manualmente. Veja ArenaAllocator.
STL Containers para std de Zig
| C++ (STL) | Zig (std) |
|---|---|
std::vector<T> | std.ArrayList(T) |
std::array<T,N> | [N]T |
std::string | []const u8 / std.ArrayList(u8) |
std::unordered_map<K,V> | std.HashMap(K,V,...) |
std::map<K,V> | std.TreeMap (ou sorted array) |
std::optional<T> | ?T |
std::variant<A,B,C> | union(enum) { a: A, b: B, c: C } |
Herança para Composição
C++
class Forma {
public:
virtual double area() const = 0;
virtual ~Forma() = default;
};
class Circulo : public Forma {
double raio;
public:
Circulo(double r) : raio(r) {}
double area() const override { return 3.14159 * raio * raio; }
};
Zig
// Opção 1: Comptime (polimorfismo estático)
fn calcularArea(forma: anytype) f64 {
return forma.area();
}
const Circulo = struct {
raio: f64,
pub fn area(self: Circulo) f64 {
return std.math.pi * self.raio * self.raio;
}
};
// Opção 2: Interface manual (polimorfismo dinâmico)
const Forma = struct {
ptr: *anyopaque,
areaFn: *const fn (*anyopaque) f64,
pub fn area(self: Forma) f64 {
return self.areaFn(self.ptr);
}
};
Namespaces
C++
namespace math {
namespace linear {
class Matrix { /* ... */ };
}
}
Zig
// math/linear.zig
pub const Matrix = struct {
// ...
};
// main.zig
const linear = @import("math/linear.zig");
const m = linear.Matrix{};
Zig usa o sistema de arquivos como namespaces. Cada arquivo é um struct implícito.
Testes
C++
// Com Google Test
TEST(CalculadoraTest, Soma) {
EXPECT_EQ(soma(2, 3), 5);
}
Zig
test "soma" {
try std.testing.expectEqual(@as(i32, 5), soma(2, 3));
}
Veja Testes Unitários Básicos e Testes com Allocator.
Conclusão
A migração de C++ para Zig simplifica o código eliminando camadas de abstração (herança, templates complexos, exceções). O resultado é código mais previsível, mais fácil de entender e debugar, com performance equivalente ou superior.
Para build system, veja Migrar de CMake para build.zig. Para a estratégia geral, consulte Guia de Migração: C para Zig (muitos padrões se aplicam).