Comente sobre o que você entendeu, para debatermos.
----------------------------------------------------------------------------------------------------------------
Nem direita, nem esquerda. Quando se trata de corrupção o Brasil é ambidestro.
(anônimo)
Encryption works. Properly implemented strong crypto systems are one of the few things that you can rely on. Unfortunately, endpoint security is so terrifically weak that NSA can frequently find ways around it. — Edward Snowden
3. Re: Sobrecarga do operador (), qual uso comum que é dado a ele? [RESOLVIDO]
uilianriesusa Linux Mint
Post recolhido
Enviado em 11/08/2016 - 09:01h
Para functors
#include <iostream>
struct Foo {
int operator()(int value) {
std::cout << "Viva o Linux" << '\n';
return value;
}
};
int main()
{
Foo foo;
Bar bar(foo);
bar.m_callback();
Bar baz(qux);
baz.m_callback();
return 0;
}
A saída será:
Viva o Linux
Viva o Linux
Viva o C++
Veja que o construtor de Bar necessita de uma função como parâmetro. Poderiamos simplemente passar um ponteiro de função, funcionaria igual, mas utilizando operator(), estamos adequando o uso de objeto de função.
Veja que em todos os casos, passamos um objeto e tratamos como uma função.
--
Uilian Ries
Linux Counter: 521986
4. Re: Sobrecarga do operador (), qual uso comum que é dado a ele?
paulo1205usa Ubuntu
Post recolhido
Enviado em 11/08/2016 - 09:02h
Sobrecarregar o operador de chamada de função é muito útil, e o mais legal é que, ao contrário dos demais operadores, que são todos unários ou binários, você pode passar quantos argumentos quiser e dos tipos que quiser.
Um uso muito comum é para criar classes de objetos com comportamento equivalente a funções. Um caso particular disso é para criar predicados (que são funções booleanas que retornam verdadeiro se uma determinada condição for verdadeira, a partir de testes sobre seus argumentos). Em C++, esses predicados costumam ser usados com algoritmos encontrados na biblioteca padrão, no cabeçalho <algorithm>.
Suponha que você tem uma coleção de números inteiros guardada em int_col, que é do tipo std::vector<int>, e que você queira copiar para uma segunda coleção int_col2 todos os números em int_col que sejam múltiplos de 3, usando um algoritmo de <algorithm> que invoca um predicado. Eis abaixo um jeito de fazer, indicando uma função, por meio de um ponteiro de função, como predicado.
void f(){
std::vector<int> int_col{ /* um monte de valores inteiros */};
std::vector<int> int_col2;
/* ... */
auto vi=int_col.cbegin();
while((vi=std::find_if(vi, int_col.cend(), is_mult_of_3))!=int_col.cend())
int_col2.push_back(*vi);
/* ... */
}
Eu usei uma função porque o predicado compara o argumento sempre com um valor fixo. Como eu usei std::find_if(), o predicado só pode receber um argumento. Mas se eu quisesse testar se o valor é múltiplo de um valor arbitrário, que eu pudesse definir a qualquer momento, mesmo durante a execução do programa, como eu poderia fazer?
Um jeito é criar uma classe com um construtor que receba as condições “fixas” do pedicado como argumento na hora de criar o objeto, e definir uma sobrecarga do operador () para esse objeto, de modo que ele possa ser usado como se fosse uma função (no nosso caso, com o papel de predicado). Veja como.
#Include <algorithm>
#include <vector>
class is_mult {
private:
int _n;
public:
is_multi(int n): _n(n) { }
bool operator()(int n){ return !(n%_n); }
};
void f(int n){
std::vector<int> int_col{ /* um monte de valores inteiros */};
std::vector<int> int_col2;
is_mult is_mult_of_n(n); // Por meio do construtor, defino como ai funcionar o predicado.
/* ... */
auto vi=int_col.cbegin();
// Agora, em vez de ponteiro de função, eu passo (uma referência a) o objeto.
while((vi=std::find_if(vi, int_col.cend(), is_mult_of_n))!=int_col.cend())
int_col2.push_back(*vi);
/* ... */
}
Se você já ouviu falar de lambdas no C++11, saiba que eles nada mais são do que objetos-função de classes anônimas, criadas automaticamente. O código da função g(), abaixo, produz o mesmo efeito da função f() mostrada acima, e o lambda gera uma classe anônima rigorosamente equivalente à classe is_mult.
void g(int n){
std::vector<int> int_col{ /* um monte de valores inteiros */};
std::vector<int> int_col2;
/*
O “n” dentro dos colchetes cria um membro anônimo na
classe e um construtor que copia o valor de n para esse
membro anônimo. Quando usado no corpo do lambda, o
“n” é substituído não pelo valor que n tem dentro de g(),
mas sim pelo membro anônimo da classe correspondente
ao argumento n passado ao construtor.
*/
auto is_mult_of_n=[n](int m){ return !(m%n); };
/* ... */
auto vi=int_col.cbegin();
while((vi=std::find_if(vi, int_col.cend(), is_mult_of_n))!=int_col.cend())
int_col2.push_back(*vi);
/* ... */
}
Ou ainda o seguinte, também rigorosamente equivalente, mas com o lambda expresso diretamente no invocação da função que vai usá-lo como argumento.
void g(int n){
std::vector<int> int_col{ /* um monte de valores inteiros */};
std::vector<int> int_col2;
/* ... */
auto vi=int_col.cbegin();
while((vi=std::find_if(vi, int_col.cend(), [n](int m){ return !(m%n); }))!=int_col.cend())
int_col2.push_back(*vi);
/* ... */
}
Mas predicados, em particular, ou objetos-função, em geral, não esgotam o uso do operador ().
Sempre que, por uma questão de notação, o nome do objeto puder ser aplicado a um conjunto de argumentos diretamente, pode-se usar operador () para não ter de inventar um método com um nome redundante.
Um exemplo que eu já vi e já usei foi o de uma classe de polinômios. Na Matemática, quando você define um polinômio, o faz com algo numa forma parecida com isto “P(x)=2x²-4x+5”, e quando quer indicar o valor do polinômio no quando x vale, por exemplo, 1.2, você diz apenas “P(1.2)”. Em C++, com a sobrecarga de operator()()você pode conseguir uma notação muito próxima dessa notação matemática.
polynomial<double> P{2, -4, 5}; // P(x)=2x²-4x+5;
/* ... */
std::cout << "P(1.2)=" << P(1.2); // Calcula e imprime o polinômio P(x) quando x=1.2;
Sem a sobrecarga, talvez nós tivéssemos de inventar um método chamado evaluate_at(), e usar algo como P.evaluate_at(1.2), que além de fugir da notação matemática tradicional, também é mais cansativo de escrever e de ler.
Outro uso que eu já vi e já usei foi como método de acesso a elemento em classes que representam arrays multidimensionais, em que você não deseja dar ao usuário acesso a níveis separados do array. Compare os dois exemplos abaixo para entender.
// Array 3D, modo 1: array de array de array.
#include <array>
std::array<std::array<std::array<double, Z_size>, Y_size>, X_size> array3d;
// Poderia ser std::vector<std::vector<std::vector<double>>>, mas seria pior ainda!
void f(){
std::cout << array3d[x][y][z] << '\n'; // Acesso a um elemento. OK.
std::cout << array3d[x][y].data() << '\n'; // Acesso a um array inteiro. Isso é OK?
std::cout << array3d[x].data() << '\n'; // Acesso a um array de arrays. Isso é OK?
std::cout << array3d.data() << '\n'; // Acesso bruto a todos os dados. Isso é OK?
// Com vector em lugar de arrays, seria pior ainda, porque cada linha, coluna ou
// profundidade poderia ter números de elementos diferentes dos demais!!
}
// Modo 2: com uma classe que encapsula totalmente a representação do array 3D.
// Com o encapsulamento, perde-se a possibilidade de indexar os elementos
// com múltiplos níveis de colchetes, de modo que se usa o operador ().
template <unsigned X_sz, unsigned Y_sz, unsigned Z_sz, typename T>
class array3d_t {
private:
T [X_sz*Y_sz*Z_sz] _data;
public:
T &operator()(unsigned x, unsigned y, unsigned z){ return _data[(x*Y_sz+y)*Z_sz+z]; }
};
void f(){
std::cout << array3d(x, y, z) << '\n'; // Acesso somente a cada elemento, nunca a subarrays.
}
Mas a sua imaginação é o limite. O fato de permitir qualquer quantidade de elementos de quaisquer tipos dá a você inúmeras possibilidades.
Eu só recomendo cautela para que a notação faça sentido. Espera-se que a sobrecarga operador () -- como, aliás, a de qualquer operador -- ajude o código a ser naturalmente mais inteligível, não que o torne mais obscuro.
5. Re: Sobrecarga do operador (), qual uso comum que é dado a ele? [RESOLVIDO]
XProtomanusa Fedora
Post recolhido
Enviado em 14/08/2016 - 22:46h
Obrigado listeiro_037, uilianries e paulo1205.
____________________
“Mas nós sabemos que a razão de a maioria estar aqui, é a nossa afinidade com a desobediência!” (Morpheus)