{"id":2036,"date":"2022-03-08T23:37:52","date_gmt":"2022-03-09T02:37:52","guid":{"rendered":"https:\/\/elemarjr.com\/cppmoderno\/?p=2036"},"modified":"2025-06-18T16:07:07","modified_gmt":"2025-06-18T19:07:07","slug":"evitando-efeitos-colaterais-indesejaveis-com-const-correctness-apendice-c-v-1-0","status":"publish","type":"post","link":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/evitando-efeitos-colaterais-indesejaveis-com-const-correctness-apendice-c-v-1-0\/","title":{"rendered":"Evitando &#8220;efeitos colaterais indesej\u00e1veis&#8221; com &#8220;const correctness&#8221; \/ Ap\u00eandice C v 1.0"},"content":{"rendered":"<p><strong>Poucas linguagens permitem ao programador expressar suas inten\u00e7\u00f5es, manipulando dados em mem\u00f3ria, com tanta riqueza quanto C++.<\/strong> Exemplo disso, \u00e9 uma funcionalidade da linguagem, extremamente poderosa, ausente na maioria das linguagens\u00a0<em>mainstream<\/em>, conhecida como <em>const correctness.<\/em><\/p>\n<h2>Mais do mesmo&#8230;<\/h2>\n<p><strong>Para entender o poder de\u00a0<em>const correctness<\/em>, vamos, antes, revisitar duas <em>features <\/em>mais simples, comuns a quase todas as linguagens: passagem de par\u00e2metros &#8220;por valor&#8221; e &#8220;por refer\u00eancia&#8221;<\/strong><\/p>\n<p>No exemplo que segue, uma\u00a0<em>string <\/em>\u00e9 passada &#8220;por valor&#8221;. Ou seja, um &#8220;objeto-c\u00f3pia&#8221; \u00e9 criado quando a fun\u00e7\u00e3o \u00e9 acionada e destru\u00eddo no momento em que ela retorna.<\/p>\n<pre>#include &lt;iostream&gt;\r\n#include &lt;string&gt;\r\n\r\n\/\/ s is passed by value\r\nvoid f(std::string s) {\r\n  s.clear();\r\n  std::cout &lt;&lt; \"F: S is empty: \" &lt;&lt; s.empty() &lt;&lt; std::endl;\r\n}\r\n\r\nint main() {\r\n  std::string s(\"This is my string\");\r\n  f(s);\r\n  std::cout &lt;&lt; \"main: S is empty: \" &lt;&lt; s.empty() &lt;&lt; std::endl;\r\n  std::cout &lt;&lt; \"main: S value   : \" &lt;&lt; s &lt;&lt; std::endl;\r\n  return 0;\r\n}\r\n\r\n\/\/ OUTPUT\r\n\/\/ F: S is empty   : 1\r\n\/\/ main: S is empty: 0\r\n\/\/ main: S value   : This is my string\r\n<\/pre>\n<p>O resultado desse c\u00f3digo \u00e9 que a <em>string<\/em> original, criada na\u00a0fun\u00e7\u00e3o <code>main<\/code>, permanece seguramente inalterada, enquanto uma nova <em>string<\/em> \u00e9 manipulada na fun\u00e7\u00e3o <code>f<\/code> .<\/p>\n<p>J\u00e1 no exemplo que segue, \u00e9 indicado que o valor recebido como par\u00e2metro \u00e9 passado &#8220;por refer\u00eancia&#8221;.<\/p>\n<pre>#include &lt;iostream&gt;\r\n#include &lt;string&gt;\r\n\r\n\/\/ s is a reference\r\nvoid f(std::string&amp; s) {\r\n  s.clear();\r\n  std::cout &lt;&lt; \"F: S is empty: \" &lt;&lt; s.empty() &lt;&lt; std::endl;\r\n}\r\n\r\nint main() {\r\n  std::string s(\"This is my string\");\r\n  f(s);\r\n  std::cout &lt;&lt; \"main: S is empty: \" &lt;&lt; s.empty() &lt;&lt; std::endl;\r\n  std::cout &lt;&lt; \"main: S value   : \" &lt;&lt; s &lt;&lt; std::endl;\r\n  return 0;\r\n}\r\n\r\n\/\/ OUTPUT\r\n\/\/ F: S is empty   : 1\r\n\/\/ main: S is empty: 1\r\n\/\/ main: S value   : \r\n<\/pre>\n<p>O resultado do c\u00f3digo acima \u00e9 que a <em>string<\/em> original, criada na\u00a0fun\u00e7\u00e3o <code>main<\/code>, \u00e9 a mesma recebida e manipulada na fun\u00e7\u00e3o <code>f<\/code> .<\/p>\n<h2>Um problema quase sem solu\u00e7\u00e3o, fora do C++<\/h2>\n<p><strong>Como previnir que um programador realize altera\u00e7\u00f5es, inadvertidamente, no estado de um objeto passado como par\u00e2metro &#8220;por refer\u00eancia&#8221;?\u00a0<\/strong>Em C++, basta marcar tal par\u00e2metro como <code>const<\/code>.<\/p>\n<pre class=\"erro\">#include &lt;iostream&gt;\r\n#include &lt;string&gt;\r\n\r\n\/\/ s is a reference\r\nvoid f(const std::string&amp; s) {\r\n  s.clear();\r\n  std::cout &lt;&lt; \"F: S is empty: \" &lt;&lt; s.empty() &lt;&lt; std::endl;\r\n}\r\n\r\nint main() {\r\n  std::string s(\"This is my string\");\r\n  f(s);\r\n  std::cout &lt;&lt; \"main: S is empty: \" &lt;&lt; s.empty() &lt;&lt; std::endl;\r\n  std::cout &lt;&lt; \"main: S value   : \" &lt;&lt; s &lt;&lt; std::endl;\r\n  return 0;\r\n}\r\n<\/pre>\n<p>O que acontece aqui \u00e9 uma esp\u00e9cie de &#8220;<em>casting<\/em>&#8221; do tipo do objeto passado como par\u00e2metro, onde todos os membros n\u00e3o marcados como <code>const<\/code> tornam-se inacess\u00edveis. Qualquer viola\u00e7\u00e3o \u00e9 detectada em tempo de compila\u00e7\u00e3o, sem preju\u00edzos de performance em tempo de execu\u00e7\u00e3o.<\/p>\n<h2>Como o programador expressa &#8220;membros <code>const<\/code>&#8221; em seus tipos<\/h2>\n<p>Cabe ao programador, nas suas implementa\u00e7\u00f5es de tipos, como a indicada no exemplo que segue, apontar, ent\u00e3o, os membros que n\u00e3o realizam modifica\u00e7\u00f5es de estado (seguras para serem chamadas com <em>const<\/em>).<\/p>\n<pre>class Person {\r\n  int _age {};\r\npublic:\r\n  auto age() const { return _age; }       \/\/ this method is not allowed to mutate the object.\r\n  auto set_age(int age) { _age = age; }\r\n};\r\n<\/pre>\n<p>No exemplo acima, a implementa\u00e7\u00e3o do m\u00e9todo <code>age<\/code>, marcado com o modificador <code>const<\/code>, cumpre o contrato de n\u00e3o gerar modifica\u00e7\u00f5es de estado, afinal, apenas retorna um valor.<\/p>\n<pre class=\"erro\">class Person {\r\n  int _age {};\r\n  int _readcount{};\r\npublic:\r\n  auto age() const {\r\n    _readcount++; \r\n    return _age; \r\n  }\r\n  auto set_age(int age) { _age = age; }\r\n};\r\n<\/pre>\n<p>J\u00e1 o c\u00f3digo acima n\u00e3o ir\u00e1 compilar porque o compilador consegue identificar que o &#8220;compromisso&#8221; assumido no contrato (interface p\u00fablica da classe) n\u00e3o foi respeitado.<\/p>\n<h2>Como o programador expressa &#8220;retornos <code>const<\/code>&#8220;<\/h2>\n<p>Caso o valor retornado seja uma refer\u00eancia, tamb\u00e9m \u00e9 poss\u00edvel determinar se ela poder\u00e1, ou n\u00e3o, ser modificada.<\/p>\n<pre>class Point2\r\n{\r\n  int _x;\r\n  int _y;\r\npublic:\r\n  Point2(int x, int y) : _x(x), _y(y) {}\r\n  auto x() const { return _x; }\r\n  auto set_x(int x) { _x = x; }\r\n  auto y() const { return _y; }\r\n  auto set_y(int y) { _y = y; }\r\n};\r\n\r\nclass Circle\r\n{\r\n  Point2 _center;\r\n  double _radius;\r\npublic:\r\n  Circle(int centerX, int centerY, double radius) :\r\n    _center(Point2(centerX, centerY)), _radius(radius) {}\r\n\r\n  auto&amp; center() const { return _center; };\r\n  auto radius() const { return _radius; };\r\n};\r\n<\/pre>\n<p>No c\u00f3digo acima, por exemplo, o <em>getter<\/em> <code>center<\/code> retorna uma refer\u00eancia &#8220;constante&#8221; para um objeto do tipo <code>Point2<\/code>, evitando, com toda a seguran\u00e7a, a cria\u00e7\u00e3o de uma &#8220;c\u00f3pia&#8221; na mem\u00f3ria.<\/p>\n<pre class=\"erro\">int main()\r\n{\r\n    Circle c(11, 0, 10.0);\r\n    c.center().set_y(10); \/\/ will not compile, set_y requires a mutable object.\r\n    return 0;\r\n}\r\n<\/pre>\n<p>O exemplo acima, n\u00e3o ir\u00e1 compilar porque h\u00e1 uma tentativa de acessar um membro &#8220;n\u00e3o-const&#8221; em um tipo indicado como &#8220;const&#8221;.<\/p>\n<h2>O compilador do C++ n\u00e3o ajuda?<\/h2>\n<p><strong>Para cumprir seu compromisso de conceder ao programador o m\u00e1ximo de acesso a recursos, para obten\u00e7\u00e3o de performance \u00f3tima, em um tempo em que compiladores dispunham de poucos KB para processar bases gigantescas de c\u00f3digo, C++ j\u00e1 permitiu (e por compatibilidade retroativa, ainda permite) a escrita de c\u00f3digo &#8220;perigoso&#8221;. Entretanto, a linguagem amadureceu e hoje oferece artif\u00edcios simples para impedir grandes enganos. Um desses artif\u00edcios \u00e9 a ideia de &#8220;<em>const correctness<\/em>&#8220;.<\/strong><\/p>\n<p>Trata-se de uma solu\u00e7\u00e3o genialmente simples para impedir enganos perigosos, simplesmente permitindo que o programador expresse o que deseja do c\u00f3digo de maneira cuidadosa. <strong>O compilador do C++ n\u00e3o ajuda &#8220;direcionando&#8221; o programador para &#8220;n\u00e3o enganos&#8221;, mas garantindo que suas inten\u00e7\u00f5es, expressas no c\u00f3digo, sejam concretizadas.<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Poucas linguagens permitem ao programador expressar suas inten\u00e7\u00f5es, manipulando dados em mem\u00f3ria, com tanta riqueza quanto C++. Exemplo disso, \u00e9 uma funcionalidade da linguagem, extremamente poderosa, ausente na maioria das linguagens\u00a0mainstream, conhecida como const correctness. Mais do mesmo&#8230; Para entender o poder de\u00a0const correctness, vamos, antes, revisitar duas features mais simples, comuns a quase todas [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":2068,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"hashtags":[],"tipo":[39],"url":[],"apendices":[35],"capitulos":[],"class_list":["post-2036","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tipo-apendice","apendices-apendice-c"],"_links":{"self":[{"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/posts\/2036","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=2036"}],"version-history":[{"count":35,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/posts\/2036\/revisions"}],"predecessor-version":[{"id":2076,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/posts\/2036\/revisions\/2076"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/media\/2068"}],"wp:attachment":[{"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/media?parent=2036"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/categories?post=2036"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/tags?post=2036"},{"taxonomy":"hashtags","embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/hashtags?post=2036"},{"taxonomy":"tipo","embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/tipo?post=2036"},{"taxonomy":"url","embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/url?post=2036"},{"taxonomy":"apendices","embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/apendices?post=2036"},{"taxonomy":"capitulos","embeddable":true,"href":"https:\/\/elemarjr.com\/livros\/cpp-moderno\/wp-json\/wp\/v2\/capitulos?post=2036"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}