<div dir="ltr"><div class="gmail_extra"><div class="gmail_extra"><br><div class="gmail_quote">2016-02-23 22:38 GMT-03:00 Alejandro Mesias <span dir="ltr"><<a href="mailto:ale.mesias@gmail.com" target="_blank">ale.mesias@gmail.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><p dir="ltr">Agora só fica a dúvida como usar classe que extendem de.</p>
<p dir="ltr">class A {}<br>
class B : public B {}</p>
<p dir="ltr">A ob_a = new B;</p>
<p dir="ltr">Como ficaria isso no auto, sem new?</p></blockquote></div><br></div><div class="gmail_extra">Acho que tem algumas coisas que não ficaram claras.<br></div><div class="gmail_extra"><br></div><div class="gmail_extra">O std::make_unique<T>() é apenas um "açúcar sintático" (implementado dentro da biblioteca padrão do C++, não na linguagem em si), mas o que ele é, é basicamente esse código abaixo. Desconsidere aí os parâmetros que serão passados pro construtor, pois aí a coisa complica se quiser fazer algo genérico:<br><br></div><div class="gmail_extra">    template <class T><br></div><div class="gmail_extra">    std::unique_ptr<T> make_unique() {<br></div><div class="gmail_extra">        return std::unique_ptr<T>(new T);<br></div><div class="gmail_extra">    }<br></div><div class="gmail_extra"><br></div>Então, sim, tem um new na jogada. E tem um delete também, dentro do std::unique_ptr (é similar àquela classe File que coloquei no outro email).<br><br>O auto facilita a declaração de uma variável dentro dum bloco de comandos por fazer a inferência do tipo. Escrever:<br><br></div><div class="gmail_extra">   auto foo = std::make_unique<Foo>();<br><br>É a mesma coisa que:<br><br></div><div class="gmail_extra">   std::unique_ptr<Foo> foo = std::make_unique<Foo>();<br><br></div><div class="gmail_extra">Que acaba sendo a mesma coisa que:<br><br></div><div class="gmail_extra">   std::unique_ptr<Foo> foo = std::unique_ptr<Foo>(new Foo);<br><br></div><div class="gmail_extra">Isso foi fácil fazer na linguagem, considerando que o compilador usava a informação de tipo pra berrar na cara do usuário se estivesse atribuindo pra um tipo errado pra, ao invés disso, declarar uma variável daquele tipo.<br></div><div class="gmail_extra"><br></div><div class="gmail_extra">Agora à dúvida da classe herdada: não tem como fazer o que você quer; pelo menos não do jeito que escreveu.<br><br>Pra entender o motivo, tem que lembrar do fato que uma struct e uma classe em C++ são a mesma coisa. A única coisa que muda é a visibilidade padrão dos elementos -- numa struct, são todos public por padrão, numa classe são todos private por padrão. Structs inclusive podem ter métodos, virtuais ou não, herdar, etc.<br><br>E, quando uma classe herda de outra, acontece basicamente isso o seguinte. Vamos pra C agora, pra deixar a coisa mais fundamental:<br><br></div><div class="gmail_extra">    struct A {<br></div><div class="gmail_extra">       int foo;<br>    };<br><br></div><div class="gmail_extra">    struct B {<br></div><div class="gmail_extra">       struct A super;<br></div><div class="gmail_extra">       int bar;<br>    };<br></div><div class="gmail_extra"><br></div><div class="gmail_extra">As coisas complicam um pouco mais por causa de métodos virtuais herança múltipla, é claro, mas o ponto é, fazer isso não vai rolar:<br><br></div><div class="gmail_extra">    struct B acme;<br></div><div class="gmail_extra">    struct A meep = acme;<br></div><div class="gmail_extra"><br></div><div class="gmail_extra">Pois A e B são de tipos diferentes.<br><br>Entretanto, "super" na struct B é o primeiro elemento e ele tá todo lá (não é um ponteiro pra um elemento da struct A), então tem espaço reservado no começo dela pra caber todo o int foo dentro de acme. Portanto, se meep for assim:<br><br></div><div class="gmail_extra">    struct A *meep = (A *)acme;<br></div><div class="gmail_extra"><br></div><div class="gmail_extra">Você teria acesso ao int foo de dentro do acme, que é na verdade um struct B, que não tem um int foo.<br><br></div><div class="gmail_extra">O que acontece aí é que não é que precisa de um new, é que precisa dum ponteiro e um cast, mesmo que implícito (por causa da herança no C++).<br><br></div><div class="gmail_extra">Convém lembrar também que o primeiro trecho de código declarando meep e o segundo declarando *meep não são compatíveis. Tanto em C quanto em C++, no primeiro trecho, se acme e meep fossem do mesmo tipo, rolaria uma cópia. Em C seria byte-a-byte mesmo -- e em C++ a mesma coisa, a não ser que tenha um construtor de cópia, que seria chamado.<br><br></div><div class="gmail_extra">Voltando pro C++, aliás, se tivéssemos isso:<br><br></div><div class="gmail_extra">    auto foo = std::make_unique<A>();<br><br></div><div class="gmail_extra">E eu fizesse:<br><br></div><div class="gmail_extra">    auto bar = foo;<br><br></div><div class="gmail_extra">Isso aí daria pau, pois um std::unique_ptr<T>() não pode ser copiado. Entretanto, se quisesse ter duas referências pra um mesmo struct A e não ter que ficar sacando quem tá usando ou não pra saber qual é a hora de matar, tem que usar um outro cara similar, chamado std::shared_ptr<T>. A ideia é a mesma:<br><br></div><div class="gmail_extra">    auto foo = std::make_shared<A>();<br></div><div class="gmail_extra">    auto bar = foo;<br><br></div>Eu não estaria fazendo uma cópia do struct A, mas sim rodando o construtor de cópia do std::shared_ptr<T>(), que na real faz exatamente o que o nome diz: vai continuar existindo um único struct A que o std::make_shared() alocou, mas a contagem de referência interna do std::shared_ptr<A> que ele retornou vai ser incrementada na cópia. Na destrição, vai ser decrementada. Se chegar em 0, o destrutor do struct A é chamado.<br><br><div class="gmail_extra">-- <br><div class="gmail_signature">  Leandro</div>
</div></div>