Enviado em 30/09/2016 - 03:34h 
		uNclear escreveu:
Galera alguém poderia me explicar como funciona as funçoes as quais trabalham com endereços const como por exemplo:
 vectorx& vectorx::operator*(const int& n);   
queria entender como isso funcionaria por baixo dos panos tipo o retorno do endereço de algum objeto vectorx, tanto quanto a entrada do endereço n na função, e o porque de coloca-ló como const.
Espero ter sido claro. Obrigado 
Antes de começar a discussão, uma nota histórica introdutória.  C++ se baseou originalmente na linguagem C.  O C, até hoje, passa todos os argumentos de funções para dentro delas por valor (isto é, os valores dos argumentos são 
copiados  para dentro da função).  Semelhantemente, o valor retornado por uma função também é uma cópia do valor da expressão passada ao comando 
return .  Se você quiser acesso aos objetos originais, tem manualmente de obter seus endereços e passar cópias desses endereços, e então usar o operador de indireção (
* ) para aceder aos objetos a partir dos endereços recebidos.
Como herdeiro do C, o C++ herdou esse comportamento de cópia de valores quando se usa sintaxe semelhante àquela do C.  No entanto, o C++ introduziu também passagem por referência (em vez de cópias dos valores, passam-se os próprios objetos), através de uma sintaxe nova, não existente no C.
Você falou em “por baixo dos panos”.  Eu não gosto muito desse termo, porque tem conotação de safadeza ou de corrupção.  Prefiro falar em “por trás das cortinas”, que tem o sentido daquilo que acontece num teatro e que é fundamental para o bom andamento do espetáculo, mas que nem sempre aparece aos olhos do público.
Você está correto na ideia geral: quando se usam referências, o que é passado internamente de um lado para outro, quer como argumento, quer como entidade retornada, são os endereços dos objetos referidos por nome.  Esse trânsito de endereços acontece apenas nos bastidores do código compilado.  Para você, programador em C++, a forma de operar com objetos referenciados é como se fosse uma variável comum de um tipo que geralmente não é ponteiro.
Considere o seguinte caso.
int a=0; 
As três operações de incremento operam sobre o mesmo dado, contido numa única posição de memória.  O dado é originalmente designado pela variável 
a , de modo que a primeira operação de incremento atua diretamente sobre a variável.  Para associar o ponteiro ao dado e para operar com o valor apontado, você tem de ser explícito tanto na hora de obter o endereço do dado original quanto na de dizer que quer voltar ao dado a partir do endereço.  Já com a referência, o compilador faz a implicitamente substituição da obtenção do endereço (sobre 
a , na hora da declaração de 
ra ) e do regresso ao dado original (penúltima linha).
Na prática, é como se a referência fosse um sinônimo exato da variável original que ela referencia.
O caso mais comum de uso de referência, no entanto, é com funções.  Veja duas formas de fazer uma função para intercâmbio de valores de duas variáveis.
// As duas versões podem ter o mesmo nome porque os argumentos são diferentes! 
É bem possível que, se você examinar o código compilado das duas funções, eles sejam absolutamente idênticos, lidando internamente com endereços.  Também as formas de chamar as duas funções podem produzir exatamente o mesmo Assembly, só que numa delas você tem de lidar com os endereços explicitamente, enquanto na outra o compilador faz isso por você.
Uma função que receba referências como argumentos se livra de um inconveniente que pode existir quando se trabalha com ponteiros, que é a possibilidade de receber um argumento contendo um ponteiro nulo ou inválido.  Uma referência sempre estará associada a um objeto real.
void triplica(int *pi){ 
Por outro lado, a possibilidade de alterar o valor de um dado numa chamada de função sem indicar explicitamente no código uma passagem por referência pode causar um pouco de confusão.  Funções que possam modificar argumentos deveriam indicar isso muito bem através dos seus nomes, pelo menos.  (Em tempo, essa consideração se aplica também a argumentos ponteiros, mas o simples fato de ter de usar explicitamente o operador 
&  para indicar a passagem por referência já serve de alerta para muita gente, mesmo que o argumento acabe não sendo modificado.)
int a; 
Quanto a referências constantes, você deve usá-las quando quiser obter benefícios de referências, mesmo que não vá (ou não possa) modificar os valores originais.
void f_ri(int &); 
(Por falar em 
rvalue , o padrão C++ de 2011 (C++11) introduziu referências para 
rvalues , que são usadas sobretudo para mover dados de objetos temporários para outros objetos.  Não vou entrar em detalhes a respeito aqui, mas você pode ler um artigo muito didático em 
http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html  (em Inglês).)
Já o retorno de referências por uma função tem propósito semelhante: entregar um meio de fazer acesso a um objeto já existente, em lugar de fazer cópia dele.
A mesma coisa já se podia fazer antes, retornando um ponteiro para o objeto, em vez do seu valor, mas isso exigia algumas operações explícitas de indireção e alguns cuidados a mais, como o de verificar se o ponteiro recebido é válido antes de usá-lo para ter acesso ao dado.
Algo interessante no exemplo que você mostrou foi que você usou justamente uma função que faz sobrecarga de um operador para um tipo definido pelo usuário (embora com um ligeiro erro semântico, sobre o qual falarei mais a diante).  Eu nunca li isso em lugar nenhum, mas eu tenho a interpretação de que um grande motivador para uso de referências, assim como para a sobrecarga de operadores, é a abordagem do C++ de permitir que tipos definidos pelo usuário possam se parecer com tipos nativos.
Considere o seguinte código.
int a; 
Se, em vez de 
int , você usar qualquer outro tipo nativo (exceto 
void , e ajustando também os tipos das constantes, para fazer sentido), o trecho acima vai continuar válido.  Mas o C++ permite a você usar qualquer outro tipo mesmo, não apenas entre os tipos nativos, mas também tipos definidos por você, desde que você forneça os operadores corretos e com a semântica correta para cada tipo.
Um exemplo de classe que poderia substituir o 
int  do exemplo acima seria o seguinte.
#include <ostream> 
Por fim, o erro da função mostrada por você foi justamente um erro semântico no tipo retornado pelo operador, que dá ao objeto um sentido diferente do que teria a mesma operação sobre um tipo nativo.
Tipicamente o operador 
*  binário devolve um 
rvalue , com uma cópia do resultado da operação.  A função que você mostrou parece devolver um 
lvalue , já que devolve uma referência.
Esse erro é muito comum, por sinal: a pessoa implementa 
X::operator*(const Y &)  como se fosse 
X::operator*=(const Y &) .  É bom saber distinguir as duas coisas.