"De um construtor para outro": a eterna questão do desenvolvedor C++
\n\nImagine a situação: você está escrevendo uma classe com vários construtores. Um aceita três parâmetros, outro dois, outro um. E em todos eles a mesma lógica de inicialização se repete. Dá vontade de escrever algo como MyClass() : MyClass(42, "default") {} — e esquecer a duplicação. Mas o compilador C++ (até certo ponto) olha para isso com desconfiança. É possível chamar um construtor a partir de outro? E se sim — como?
Esta pergunta é uma das mais populares no Stack Overflow (dezenas de milhares de visualizações). Não é surpresa: o tópico de cadeias de construtores (constructor chaining) é uma verdadeira pedra no sapato para iniciantes e até mesmo para desenvolvedores experientes que migram para C++ vindos de Java ou Python. Neste artigo, vamos analisar três abordagens para resolver o problema, desde "gambiarras" até soluções elegantes do C++11, e descobrir qual delas vale a pena usar em código real.
\n\nPor que não podemos simplesmente chamar um construtor de dentro de outro?
\n\nA abordagem ingênua é assim:
\n\nclass MyClass {\npublic:\n MyClass() {\n // algum código\n }\n MyClass(int x) {\n MyClass(); // ERRO: não é permitido!\n }\n};\n\nEm C++, um construtor não é uma função comum. Ele inicializa o objeto uma única vez. Se você tentar chamar explicitamente outro construtor dentro do corpo, o compilador interpretará isso como a criação de um objeto temporário — e nada será inicializado na instância atual. O resultado: ou um erro de compilação, ou (pior) um comportamento indefinido.
\n\nAntes do padrão C++11, a única maneira legal de "compartilhar" a lógica de inicialização era chamar um método privado init(). Mas isso não era uma delegação real: os membros da classe eram primeiro inicializados pelo construtor padrão e depois sobrescritos. Ineficiente e propenso a bugs.
Abordagem nº 1: Construtores delegados (C++11 e posteriores)
\n\nA partir do C++11, o idioma ganhou um recurso tão esperado — os construtores delegados (delegating constructors). Agora é possível chamar um construtor de dentro de outro diretamente na lista de inicialização:
\n\nclass MyClass {\n int value;\n std::string name;\npublic:\n // Construtor base\n MyClass(int v, const std::string& n) : value(v), name(n) {\n std::cout << "Full init: " << value << ", " << name << "\\n";\n }\n // Construtor delegado\n MyClass(int v) : MyClass(v, "default") {}\n // Outro construtor delegado\n MyClass() : MyClass(0, "default") {}\n};\n\nComo funciona? O construtor MyClass(int v) passa o controle para o construtor MyClass(int, string). Este realiza toda a inicialização, e só então o controle retorna. Regra importante: na lista de inicialização não é permitido misturar a chamada de outro construtor com a inicialização direta de membros. Ou delegação, ou sua própria inicialização — um dos dois.
Prós e contras
\n- \n
- ✅ Elegância: o código fica limpo, sem duplicação. \n
- ✅ Desempenho: o objeto é inicializado uma vez, sem operações desnecessárias. \n
- ⚠️ Limitação: a delegação funciona apenas em uma "profundidade" — a cadeia não pode ser recursiva (você causará um estouro de pilha). \n
- ⚠️ Compatibilidade: requer C++11 ou superior. Se você estiver preso a um compilador antigo — esta opção não é para você. \n
Abordagem nº 2: Método privado init()
\n\nA maneira clássica "pré-C++11" é extrair a lógica comum para um método privado. Fica assim:
\n\nclass MyClass {\n int value;\n std::string name;\n void init(int v, const std::string& n) {\n value = v;\n name = n;\n std::cout << "Init called\\n";\n