{"id":2078,"date":"2022-03-14T15:50:58","date_gmt":"2022-03-14T18:50:58","guid":{"rendered":"https:\/\/elemarjr.com\/cppmoderno\/?p=2078"},"modified":"2025-06-18T16:06:51","modified_gmt":"2025-06-18T19:06:51","slug":"a-regra-dos-cinco-apendice-d-v-1-0","status":"publish","type":"post","link":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/a-regra-dos-cinco-apendice-d-v-1-0\/","title":{"rendered":"A regra dos cinco \/ Ap\u00eandice D v 1.0"},"content":{"rendered":"<p><strong>Em C++, uma classe deve ser respons\u00e1vel por gerenciar <span style=\"text-decoration: underline;\">completa e eficientemente<\/span>\u00a0os recursos que referencia.<\/strong><\/p>\n<p>Mem\u00f3ria, <em>threads<\/em>, conex\u00f5es com o banco de dados,\u00a0<em>mutexes<\/em>, s\u00e3o apenas alguns exemplos de recursos, potencialmente escassos, que, uma vez alocados, se n\u00e3o forem adequadamente liberados, podem levar o ambiente produtivo a exaust\u00e3o. Pior ainda, se forem liberados &#8220;antes do tempo&#8221;, podem conduzir o programa a um comportamento inv\u00e1lido.<\/p>\n<strong>Nos \u00faltimos anos, a gest\u00e3o da mem\u00f3ria, como recurso potencialmente escasso, em C++, ficou muito mais simples com o advento dos <em><a href=\"https:\/\/elemarjr.com\/cppmoderno\/apendices\/apendice-b\/\">smart pointers<\/a><\/em>.<\/strong> Entretanto, o desafio ainda persiste para todos os demais tipos de recursos que demandam aloca\u00e7\u00e3o\/libera\u00e7\u00e3o.\n<hr \/>\n<strong>C++, no lugar de ser &#8220;opinativa&#8221; (como outras linguagens) quanto a forma que recursos escassos devem ser gerenciados (adquiridos, mantidos e descartados), opta por dar controle total ao desenvolvedor.<\/strong> Essa decis\u00e3o certamente desagrada alguns e agrada muitos.\n<hr \/>\n<p><strong>Como pr\u00e1tica eficiente, nos \u00faltimos anos, convencionou-se que classes que precisam lidar com recursos potencialmente escassos devem implementar comportamentos especiais:<\/strong><\/p>\n<ol>\n<li><strong>Destrui\u00e7\u00e3o<\/strong> &#8211; liberando recursos sob &#8220;ger\u00eancia&#8221; do objeto;<\/li>\n<li><strong>Constru\u00e7\u00e3o com c\u00f3pia<\/strong>&#8211; evitando compartilhamento de controle sobre &#8220;recursos escassos&#8221; em objetos criados &#8220;copiando&#8221; um objeto existente;<\/li>\n<li><strong>Atribui\u00e7\u00e3o com c\u00f3pia <\/strong><em>&#8211; <\/em>evitando compartilhamento de controle sobre &#8220;recursos escassos&#8221; em objetos com valores copiados de\u00a0 um objeto existente;<\/li>\n<li><strong>Constru\u00e7\u00e3o com transfer\u00eancia<\/strong>&#8211; evitando aloca\u00e7\u00f5es desnecess\u00e1rias de recursos escassos, quando um objeto novo estiver sendo criado &#8220;copiando&#8221; dados de outro, pr\u00e9-existente, que n\u00e3o ser\u00e1 mais utilizado;<\/li>\n<li><b>Atribui\u00e7\u00e3o com transfer\u00eancia <\/b>&#8211; evitando aloca\u00e7\u00f5es desnecess\u00e1rias de recursos escassos, quando um objeto estiver &#8220;copiando&#8221; dados de outro, pr\u00e9-existente, que n\u00e3o ser\u00e1 mais utilizado;<\/li>\n<\/ol>\n<h2>Destrui\u00e7\u00e3o<\/h2>\n<div class=\"nota-alerta\">\r\n<table class=\"tabelaalerta\" style=\"width: 100%;\">\r\n<tbody>\r\n<tr>\r\n<td class=\"nota-coluna-1\" valign=\"top\"><img decoding=\"async\" class=\"img-citacao\" src=\"\/cppmoderno\/wp-content\/uploads\/2021\/11\/ico-citacao-2-1.png\" alt=\"\" width=\"60\" height=\"60\" \/><\/td>\r\n<td class=\"nota-coluna-2\"><img decoding=\"async\" class=\"nota-img\" src=\"\/cppmoderno\/wp-content\/uploads\/2021\/11\/ico-citacao-2-1.png\" alt=\"\" width=\"60\" height=\"60\" \/> <em>Se pegar emprestado, devolva, meu filho.<\/em>\r\n<p><strong>minha m\u00e3e<\/strong><\/p>\r\n<\/td>\r\n<\/tr>\r\n<\/tbody>\r\n<\/table>\r\n<\/div>\n<p><strong>Comecemos com uma regra bem simples: se um objeto alocar qualquer\u00a0recurso potencialmente escasso, dever\u00e1 garantir sua libera\u00e7\u00e3o.<\/strong><\/p>\n<pre>#include &lt;initializer_list&gt;\r\n#include &lt;iostream&gt;\r\n\r\nclass Buffer {\r\npublic:\r\n  Buffer(const std::initializer_list&lt;float&gt;&amp; values)\r\n          : _size{values.size()} {\r\n    _data_ptr = new float[values.size()];\r\n    std::copy(values.begin(), values.end(), _data_ptr);\r\n  }\r\n\r\n  auto begin() const { return _data_ptr; }\r\n  auto end() const { return _data_ptr + _size; };\r\n\r\n  ~Buffer() {\r\n    delete [] _data_ptr;\r\n    _data_ptr = nullptr;\r\n  }\r\n\r\nprivate:\r\n  size_t _size{0};\r\n  float* _data_ptr{nullptr};\r\n};\r\n\r\nint main() {\r\n  auto data = Buffer({1.0f, 2.0f, 3.0f, 4.0f, 5.0f}); \r\n\r\n  for (auto elem : data) {\r\n    std::cout &lt;&lt; elem &lt;&lt; std::endl;\r\n  }\r\n}\r\n<\/pre>\n<p>No exemplo acima, mem\u00f3ria, um recurso escasso, \u00e9 alocada pelo construtor e liberada pelo destrutor.<\/p>\n<h2>Constru\u00e7\u00e3o com c\u00f3pia<\/h2>\n<div class=\"nota-alerta\">\r\n<table class=\"tabelaalerta\" style=\"width: 100%;\">\r\n<tbody>\r\n<tr>\r\n<td class=\"nota-coluna-1\" valign=\"top\"><img decoding=\"async\" class=\"img-citacao\" src=\"\/cppmoderno\/wp-content\/uploads\/2021\/11\/ico-citacao-2-1.png\" alt=\"\" width=\"60\" height=\"60\" \/><\/td>\r\n<td class=\"nota-coluna-2\"><img decoding=\"async\" class=\"nota-img\" src=\"\/cppmoderno\/wp-content\/uploads\/2021\/11\/ico-citacao-2-1.png\" alt=\"\" width=\"60\" height=\"60\" \/> <em>N\u00e3o estrague o brinquedo do amiguinho.<\/em>\r\n<p><strong>minha m\u00e3e<\/strong><\/p>\r\n<\/td>\r\n<\/tr>\r\n<\/tbody>\r\n<\/table>\r\n<\/div>\n<p>A classe <code>Buffer<\/code>, implementada no exemplo acima, parece estar fazendo &#8220;tudo certo&#8221; &#8211; ela &#8220;aloca&#8221; um determinado espa\u00e7o na mem\u00f3ria em sua inicializa\u00e7\u00e3o, no m\u00e9todo construtor, e &#8220;libera&#8221; mem\u00f3ria, mais tarde, ao sair de contexto, no m\u00e9todo destrutor. Entretanto, este c\u00f3digo n\u00e3o est\u00e1 protegido contra c\u00f3pias.<\/p>\n<pre>int main() {\r\n  auto data = Buffer({1.0f, 2.0f, 3.0f, 4.0f, 5.0f});\r\n\r\n  {\r\n    auto copy = data;\r\n  }\r\n\r\n  for (auto elem : data) {\r\n    std::cout &lt;&lt; elem &lt;&lt; std::endl;\r\n    \/\/ FAIL!\r\n  }\r\n}\r\n<\/pre>\n<p>No c\u00f3digo acima, a vari\u00e1vel <code>copy<\/code> recebe uma &#8220;c\u00f3pia&#8221; do objeto apontado em <code>data<\/code>. Afinal, o programador fez a op\u00e7\u00e3o por realizar a aloca\u00e7\u00e3o na <em>stack<\/em>. Assim, todas as refer\u00eancias presentes no objeto <code>data<\/code>\u00a0s\u00e3o &#8220;replicadas&#8221; no objeto <code>copy<\/code> e, infelizmente, ser\u00e3o destru\u00eddas assim que o contexto de <code>copy<\/code> for encerrado &#8211; gerando comportamento inv\u00e1lido.<\/p>\n<pre>#include &lt;initializer_list&gt;\r\n#include &lt;iostream&gt;\r\n\r\nclass Buffer {\r\npublic:\r\n  Buffer(const std::initializer_list&lt;float&gt;&amp; values)\r\n          : _size{values.size()} {\r\n    _data_ptr = new float[values.size()];\r\n    std::copy(values.begin(), values.end(), _data_ptr);\r\n  }\r\n\r\n  \/\/ copy constructor\r\n  Buffer(const Buffer&amp; other) : _size{other._size} {\r\n    _data_ptr = new float[_size];\r\n    std::copy(other._data_ptr, other._data_ptr + _size, _data_ptr);\r\n  }\r\n\r\n  auto begin() const { return _data_ptr; }\r\n  auto end() const { return _data_ptr + _size; };\r\n\r\n  ~Buffer() {\r\n    delete [] _data_ptr;\r\n    _data_ptr = nullptr;\r\n  }\r\n\r\nprivate:\r\n  size_t _size{0};\r\n  float* _data_ptr{nullptr};\r\n};\r\n<\/pre>\n<p>A sa\u00edda, como \u00e9 indicado no c\u00f3digo acima,\u00a0 \u00e9 implementar um construtor de c\u00f3pia que &#8220;duplica&#8221; as refer\u00eancias protegendo-as.<\/p>\n<p>Eventualmente, outra alternativa seria &#8220;deletar&#8221; o construtor de c\u00f3pia tornando tal comportamento a c\u00f3pia n\u00e3o autorizada.<\/p>\n<pre class=\"erro\">Buffer::Buffer(const Buffer&amp; other) = delete;\r\n\r\nint main() {\r\n  auto data = Buffer({1.0f, 2.0f, 3.0f, 4.0f, 5.0f});\r\n\r\n  {\r\n    \/\/ No copies!\r\n    auto copy = data;\r\n  }\r\n\r\n  for (auto elem : data) {\r\n    std::cout &lt;&lt; elem &lt;&lt; std::endl;\r\n  }\r\n}\r\n<\/pre>\n<h2>Atribui\u00e7\u00e3o com c\u00f3pia<\/h2>\n<p>C++ \u00e9 tremendamente eficiente em &#8220;poupar mem\u00f3ria&#8221;, mas, as vezes, essa &#8220;obsess\u00e3o&#8221; da linguagem acaba se revertendo em dor de cabe\u00e7a.<\/p>\n<p>A implementa\u00e7\u00e3o da classe <code>Buffer<\/code>, com um construtor de c\u00f3pia, ainda resulta em comportamento indesejado no c\u00f3digo que segue:<\/p>\n<pre>int main() {\r\n  auto data = Buffer({1.0f, 2.0f, 3.0f, 4.0f, 5.0f});\r\n\r\n  {\r\n    auto copy = data;\r\n    data = copy;\r\n  }\r\n\r\n  for (auto elem : data) {\r\n    std::cout &lt;&lt; elem &lt;&lt; std::endl;\r\n    \/\/ FAIL\r\n  }\r\n}\r\n<\/pre>\n<p>No exemplo, ao reatribuir valor a vari\u00e1vel <code>data<\/code>, no lugar de ser criada uma nova inst\u00e2ncia de <code>Buffer<\/code>, C++, por padr\u00e3o, copia os valores de <code>copy<\/code> para <code>data<\/code>. Obviamente, o <em>array<\/em> referenciado em <code>copy<\/code> \u00e9 destru\u00eddo gerando comportamento potencialmente inesperado.<\/p>\n<p>A sa\u00edda \u00e9 gerar uma sobrecarga do operador de atribui\u00e7\u00e3o.<\/p>\n<pre>#include &lt;initializer_list&gt;\r\n#include &lt;iostream&gt;\r\n\r\nclass Buffer {\r\npublic:\r\n  Buffer(const std::initializer_list&lt;float&gt;&amp; values)\r\n          : _size{values.size()} {\r\n    _data_ptr = new float[values.size()];\r\n    std::copy(values.begin(), values.end(), _data_ptr);\r\n  }\r\n\r\n  \/\/ copy constructor\r\n  Buffer(const Buffer&amp; other) : _size{other._size} {\r\n    _data_ptr = new float[_size];\r\n    std::copy(other._data_ptr, other._data_ptr + _size, _data_ptr);\r\n  }\r\n\r\n  \/\/ copy assignment\r\n  auto&amp; operator=(const Buffer&amp; other) {\r\n    delete [] _data_ptr;\r\n    _data_ptr = new float[other._size];\r\n    _size = other._size;\r\n    std::copy(other._data_ptr, other._data_ptr + _size, _data_ptr);\r\n    return *this;\r\n  }\r\n\r\n  auto begin() const { return _data_ptr; }\r\n  auto end() const { return _data_ptr + _size; };\r\n\r\n  ~Buffer() {\r\n    delete [] _data_ptr;\r\n    _data_ptr = nullptr;\r\n  }\r\n\r\nprivate:\r\n  size_t _size{0};\r\n  float* _data_ptr{nullptr};\r\n};\r\n<\/pre>\n<p>Outra alternativa seria impedir atribui\u00e7\u00f5es.<\/p>\n<pre>auto&amp; operator=(const Buffer&amp; other) = delete;\r\n<\/pre>\n<h2>Constru\u00e7\u00e3o e atribui\u00e7\u00e3o com transfer\u00eancia<\/h2>\n<p>Qual seria o resultado l\u00f3gico do c\u00f3digo abaixo?<\/p>\n<pre>void print(Buffer data) {\r\n  for (auto elem : data) {\r\n    std::cout &lt;&lt; elem  &lt; &lt; std::endl;\r\n  }    \r\n}\r\n\r\nint main() {\r\n  print(Buffer({1.0f, 2.0f, 3.0f, 4.0f, 5.0f}));\r\n  return 0;\r\n}\r\n<\/pre>\n<p>Por padr\u00e3o, o comportamento seria a cria\u00e7\u00e3o de um objeto na <em>stack<\/em> associado a fun\u00e7\u00e3o <code>main<\/code>que seria, imediatamente, copiado para um novo objeto na <em>stack<\/em> associado a fun\u00e7\u00e3o <code>print<\/code>.<\/p>\n<p>Felizmente, quase todos os compiladores modernos reconhecem tal desperd\u00edcio e &#8220;evitam&#8221; a c\u00f3pia.<\/p>\n<div class=\"card-insight\" style=\"background-color: #f0f0f0; width: 100%; padding: 35px 30px 20px 35px; border-radius: 5px 5px 5px 5px; margin-top: 30px; margin-bottom: 35px; font-size: 16px; box-shadow: 0px 4px 0px 0px #dddddd;\">\r\n<p style=\"font-size: 24px; font-weight:bold; line-height: 28px; font-family: Lufga;\">Use refer\u00eancias sempre que poss\u00edvel<\/p>\r\n<\/p>\n<p><strong>O c\u00f3digo a seguir possui s\u00f3 m\u00e9ritos.<\/strong> A fun\u00e7\u00e3o <code>print<\/code> recebe uma refer\u00eancia somente leitura para um objeto.<\/p>\n<pre>void print(const Buffer&amp; data) {\r\n  for (auto elem : data) {\r\n    std::cout &lt;&lt; elem &lt;&lt; std::endl;\r\n  }    \r\n}\r\n\r\nint main() {\r\n  auto data = Buffer({1.0f, 2.0f, 3.0f, 4.0f, 5.0f});\r\n  print(data);\r\n  return 0;\r\n}\r\n<\/pre>\n<p>Sempre que poss\u00edvel utilize refer\u00eancias evitando c\u00f3pias desnecess\u00e1rias.<\/p>\n<p><\/div>\n<p>Eventualmente, entretanto, o compilador n\u00e3o conseguira detectar tais desperd\u00edcios, competindo ao programador fazer a gest\u00e3o.<\/p>\n<pre>void print(Buffer data) {\r\n  for (auto elem : data) {\r\n    std::cout &lt;&lt; elem &lt;&lt; std::endl;\r\n  }    \r\n}\r\n\r\nint main() {\r\n  auto data = Buffer({1.0f, 2.0f, 3.0f, 4.0f, 5.0f});\r\n  print(std::move(data));\r\n  return 0;\r\n}\r\n<\/pre>\n<p>No exemplo, utilizou-se <code>std::move<\/code> como forma a indicar que o que se deseja fazer \u00e9 uma transfer\u00eancia de recursos escassos. Tal fun\u00e7\u00e3o, utiliza-se de implementa\u00e7\u00f5es de constru\u00e7\u00e3o e movimenta\u00e7\u00e3o por transfer\u00eancia para evitar desperd\u00edcios.<\/p>\n<pre>#include &lt;initializer_list&gt;\r\n#include &lt;iostream&gt;\r\n\r\nclass Buffer {\r\npublic:\r\n  Buffer(const std::initializer_list&lt;float&gt;&amp; values)\r\n          : _size{values.size()} {\r\n    _data_ptr = new float[values.size()];\r\n    std::copy(values.begin(), values.end(), _data_ptr);\r\n  }\r\n\r\n  \/\/ copy constructor\r\n  Buffer(const Buffer&amp; other) : _size{other._size} {\r\n    _data_ptr = new float[_size];\r\n    std::copy(other._data_ptr, other._data_ptr + _size, _data_ptr);\r\n  }\r\n\r\n  \/\/ move constructor\r\n  Buffer(Buffer&amp;&amp; other) : _size{other._size}, _data_ptr{other._data_ptr} {\r\n    other._data_ptr = nullptr;\r\n  }\r\n  \r\n  \/\/ copy assignment\r\n  auto&amp; operator=(const Buffer&amp; other) {\r\n    delete [] _data_ptr;\r\n    _data_ptr = new float[other._size];\r\n    _size = other._size;\r\n    std::copy(other._data_ptr, other._data_ptr + _size, _data_ptr);\r\n    return *this;\r\n  }\r\n  \r\n  \/\/ move assignment\r\n  auto&amp; operator=(Buffer&amp;&amp; other) {\r\n    std::cout &lt;&lt; \"move copy\";\r\n    _size = other._size;\r\n    _data_ptr = other._data_ptr;\r\n    other._data_ptr = nullptr;\r\n    return *this;\r\n  }\r\n  \r\n  auto begin() const { return _data_ptr; }\r\n  auto end() const { return _data_ptr + _size; };\r\n\r\n  ~Buffer() {\r\n    delete [] _data_ptr;\r\n    _data_ptr = nullptr;\r\n  }\r\n\r\nprivate:\r\n  size_t _size{0};\r\n  float* _data_ptr{nullptr};\r\n};\r\n<\/pre>\n<h2>Bem al\u00e9m da mem\u00f3ria&#8230;<\/h2>\n<p>Todos os exemplos desse ap\u00eandice mostraram a aplica\u00e7\u00e3o da &#8220;regra dos cinco&#8221; em uma classe que gerenciava a mem\u00f3ria (um exemplo de recurso escasso). Entretanto, de todos os recursos, mem\u00f3ria \u00e9, potencialmente, o mais f\u00e1cil de gerenciar.<\/p>\n<p>As coisas podem ficar realmente desafiadoras na gest\u00e3o de recursos como <em>threads<\/em> que n\u00e3o podem ser simplesmente &#8220;duplicadas&#8221; ou transferidas. Da\u00ed a import\u00e2ncia de entender melhor cada cen\u00e1rio dar ao programador &#8220;poder para decidir&#8221;.<\/p>\n<p>O compilador pode e deve ajudar, sempre que poss\u00edvel, a evitar enganos. Mas, nem s\u00f3 com receitas prontas s\u00e3o feitos programas, principalmente em <em>systems programming<\/em>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Em C++, uma classe deve ser respons\u00e1vel por gerenciar completa e eficientemente\u00a0os recursos que referencia. Mem\u00f3ria, threads, conex\u00f5es com o banco de dados,\u00a0mutexes, s\u00e3o apenas alguns exemplos de recursos, potencialmente escassos, que, uma vez alocados, se n\u00e3o forem adequadamente liberados, podem levar o ambiente produtivo a exaust\u00e3o. Pior ainda, se forem liberados &#8220;antes do tempo&#8221;, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":2121,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"hashtags":[],"tipo":[39],"url":[37],"apendices":[36],"capitulos":[],"class_list":["post-2078","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tipo-apendice","url-permanente","apendices-apendice-d"],"_links":{"self":[{"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/posts\/2078","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/comments?post=2078"}],"version-history":[{"count":43,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/posts\/2078\/revisions"}],"predecessor-version":[{"id":2122,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/posts\/2078\/revisions\/2122"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/media\/2121"}],"wp:attachment":[{"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/media?parent=2078"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/categories?post=2078"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/tags?post=2078"},{"taxonomy":"hashtags","embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/hashtags?post=2078"},{"taxonomy":"tipo","embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/tipo?post=2078"},{"taxonomy":"url","embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/url?post=2078"},{"taxonomy":"apendices","embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/apendices?post=2078"},{"taxonomy":"capitulos","embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/capitulos?post=2078"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}