Digamos que estoy escribiendo una clase para representar vectores 3D para una aplicación de gráficos por computadora. Me gustaría poder multiplicar vectores por escalares, como se muestra a continuación:

Vector3D vec(1, 2, 3);  // The paramaters of the constructor are the x, y and z values
Vector3D vec1 = vec * 2; // Case A: result should be (2, 4, 6)
Vector3D vec2 = 2 * vec; // Case B: result should be (2, 4, 6)

Para que el caso A funcione, podría agregar la siguiente función a mi clase:

class Vector3D
{
public:
    // ...
    const Vector operator*(int scalar) const;
};

Para que el caso B funcione, tendría que crear una función fuera de mi clase:

const Vector operator*(int scalar, const Vector& vec);

Tenga en cuenta que también podría hacer que el caso A funcione agregando la siguiente función fuera de mi clase:

const Vector operator*(const Vector& vec, int scalar);

Mi pregunta es: ¿cuál es la forma más limpia de hacer esto? Podría agregar una función dentro de mi clase y una función fuera de ella, o podría agregar dos funciones fuera de mi clase. Me gusta la idea de agregar dos funciones fuera de la clase porque podría mantenerlas una al lado de la otra, lo que creo que es más claro ya que esencialmente hacen lo mismo, pero agregar una de las funciones dentro de la clase tiene algún beneficio que yo no estoy viendo?

Tenga en cuenta que los valores de retorno de mis funciones son constantes para que los usuarios no puedan hacer esto (como lo explicó Scott Meyers en su libro Effective C ++):

Vector3D vecA(1, 2, 3);
Vector3D vecB(1, 2, 3);
Vector3D vecC(1, 2, 3);

vecA * vecB = vecC;
1
user3266738 19 ene. 2018 a las 16:50

3 respuestas

La mejor respuesta

Dado que multiplicar un vector por escalar es simétrico, en el sentido de que no hay un participante "superior" o "subordinado" claro, usaría un par de sobrecargas de operadores independientes:

const Vector operator*(int scalar, const Vector& vec);
const Vector operator*(const Vector& vec, int scalar);

Cuando tiene que elegir entre sobrecargar una función miembro o sobrecargar una función independiente, la decisión debe basarse en la participación del objeto mismo en el cálculo con respecto a los parámetros de la función.

Cuando el objeto juega un papel central, mientras que los parámetros juegan un papel periférico, mantenga la función con el objeto convirtiéndolo en miembro. Cuando los parámetros de objeto y función juegan roles similares, es decir, cuando hay un grado de simetría entre los participantes, una función independiente es más apropiada.

1
dasblinkenlight 19 ene. 2018 a las 13:56

El operator* aquí es simétrico (como en a * b y b * a deberían ser operaciones equivalentes) por lo que debería preferir tener ambas funciones fuera de la clase.

Aparte de lo que mencionó, que tenerlos cerca uno del otro lo hace más limpio, también permite conversiones implícitas para cualquier operando . Por otro lado, si tenía operator* para el Caso A como una función miembro, entonces las conversiones implícitas del operando de la izquierda al tipo Vector3D no están permitidas, pero las conversiones del operando de la derecha a {{ El tipo X2}} está permitido (a menos que marque los constructores explicit, pero eso no viene al caso).

Al usar funciones gemelas que no son miembros, tenemos una simetría verdadera, ya que ambos operandos tienen la misma capacidad de convertirse al tipo Vector3D.

Por cierto, las Pautas principales de C ++ escritas por Bjarne Stroustrup y Herb Sutter también Sugiero hacer esto.

1
L.Y. Sim 19 ene. 2018 a las 14:18

El enfoque habitual es proporcionar operator *=(int) como una función miembro y luego escribir funciones libres operator*(Vector3D, int) y operator*(int, Vector3D) que lo llaman.

2
Pete Becker 19 ene. 2018 a las 14:06