14.01
2021
Há algum tempo me deparei com uma dúvida no Stack Overflow, um usuário perguntando se havia como fazer uma função que retornedois valores em C/C++. Tem como, e tem mais de uma forma de fazer.
Como exemplo vamos desenvolver funções que fazem conversão de coordenadas cartesianas (x, y) em coordenadas polares (r, a). A fórmula que faz essa conversão é simples:
r = sqrt(x * x + y * y);
a = atan2(y, x);
sqrt é uma função para raiz quadrada, e atan2 é uma função para calcular o arco-tangente através dos valores dos catetos.
C: Argumentos por Ponteiros
A forma mais simples de retornar dois valores é utilizar argumentos ponteiros:
#include <math.h>
#include <stdio.h>
void cart2polar(double x, double y, double *r, double *a) {
if (r == NULL || a == NULL)
return NULL;
*r = sqrt(x * x + y * y);
*a = atan2(y, x);
}
int main() {
double r, a = 0.0;
cart2polar(10, 10, &r, &a);
}
Neste caso é preciso tomar cuidado por conta dos parâmetros r e a serem ponteiros, é necessário verificar a validade dos pointeiros.
C: Retornar Estrutura de Dados
Neste caso vamos definir uma struct que encapsule os valores de retorno:
#include <math.h>
#include <stdio.h>
typedef struct {
double r;
double a;
} polar;
polar cart2polar(double x, double y) {
polar p;
p.r = sqrt(x * x + y * y);
p.a = atan2(y, x);
return p;
}
int main() {
polar p = cart2polar(10, 10);
printf("(10, 10) <=> %.2lf < %.2lf\n", p.r, p.a);
}
Apesar de ser necessário criar uma struct nova o código resultante é mais sucinto.
C++: Argumentos por Referência
Muito parecido com a solução de argumentos por ponteiros, porém sem a necessidade de gerenciar os ponteiros:
#include <cmath>
#include <iomanip>
#include <iostream>
void cart2polar(double x, double y, double &r, double &a) {
r = sqrt(x * x + y * y);
a = atan2(y, x);
}
int main() {
double r, a;
cart2polar(10, 10, r, a);
std::cout << "(10, 10) <=> " <<
std::fixed << std::setprecision(2) <<
r << " < " << a << std::endl;
}
Utilizando argumentos por referência não há os riscos de se utilizar ponteiros, sem riscos de acesso por ponteiro nulo, nem de vazamento de memória.
C++: Retornando std::pair
Um pouco mais refinado, utilizar std::pair é muito parecido com criar uma struct para retornar os dois valores, com a vantagem de não ser necessário declarar um novo tipo, e com os campos anônimos.
#include <cmath>
#include <iomanip>
#include <iostream>
#include <utility>
std::pair<double, double> cart2polar(double x, double y) {
double r = sqrt(x * x + y * y);
double a = atan2(y, x);
return std::pair<double, double>(r, a);
}
int main() {
auto p = cart2polar(10, 10);
std::cout << "(10, 10) <=> " <<
std::fixed << std::setprecision(2) <<
p.first << " < " << p.second << std::endl;
}
Note a utilização do std::pair, através dos atributos first e second.
C++: Classe
Uma última opção é a criação de uma classe, e neste caso um amplo leque de possibilidades. Vamos ver uma possibilidade.
#include <cmath>
class polar {
private:
double r;
double a;
public:
polar() : r(0.0), a(0.0) {}
polar(double _r, double _a) : r(_r), a(_a) { }
void set(double _r, double _a) {
r = _r;
a = _a;
}
void setByRect(double x, double y) {
r = sqrt(x * x + y * y);
a = atan2(y, x);
}
double getr() {
return r;
}
double geta() {
return a;
}
};
Mas neste caso as possibilidades são incontáveis, e a melhor solução sempre estará ligada com a forma de utilização da conversão específica.
Comentários
[…] grande uso de std::tuple é para retorno de múltiplos valores, como já abordado em outro post. Com std::tuple, std::make_tuple e std::tie é possível empacotar dois ou mais valores, com […]