4.9.16 Operadores añadidos a expresión
§1 Sinopsis
En este apartado se relacionan los operadores que aparecen a la derecha de una expresión; se incluyen en este grupo:
Operador de elemento de matriz [ ] .
Operador de invocación de función ( ) .
Selector directo de miembro . de clases, estructuras y uniones .
Selector indirecto de miembro -> de clases, estructuras y uniones .
§2 Observaciones
( ) Utilizado para agrupar expresiones o delimitar expresiones condicionales.
También indica llamadas de funciones y sus parámetros.
Sirve también para definir las funciónes-objeto ( 5.1.3a1).
{ } Principio y fin de sentencias compuestas.
[ ] Señala subíndices de matrices simples y multdimensionales
. Operador de acceso a miembros de clases, estructuras y uniones
-> Acceso a miembros de clases, estructuras y uniones.
§3 Operador de elemento de matriz
[ ]
Este operador también es denominado subíndice o selector de miembro de matriz, ya que los corchetes [ ] sirven para señalar subíndices de matrices simples y multidimensionales.
Es un operador binario (de dos operandos) m[n]. Cuando se utiliza con matrices de tipos simples (preconstruidos en el lenguaje) m es el identificador de la matriz y el índice n debe ser un entero positivo ( 4.3.1). Cuando se utiliza sobre tipos abstractos (instancias de clases), m es el identificador de una instancia de una clase M, y n puede ser cualquier valor (dependiendo de la definición dada por el programador). Pero entonces la expresión m[n] es equivalente a:
m.operator[](n);
donde operator[ ] es una función miembro no estática de M. Su definición puede ser cualquiera, pero generalmente devuelve una referencia a la clase M y acepta un int como argumento:
M::operator[] (int n) { /* ... */ }
La utilización de este operador con tipos abstractos supone que puede ser sobrecargado para operar con tipos definidos por el usuario ( 4.9.18d).
§3.1 En el caso de matrices de tipos simples, la expresión:
<exp1>[exp2]
se define como: *((exp1) + (exp2)), donde exp1 es un puntero y exp2 es un entero, o exp1 es un entero y exp2 es un puntero.
Ejemplo: la expresión arrX[3] se define como: *(arrX + 3), donde arrX es un puntero al primer elemento de la matriz; arrX + 3 es un puntero al cuarto elemento, y su indirección *(arrX + 3), es el valor del cuarto elemento de la matriz.
§3.2 En el caso de una matriz de tres dimensiones: m[2][4][3]:
m[2][4][3] == <m[2][4]> [3] == *( ( m[2][4] ) + 3 )
m[2][4] == <m[2]> [4] == *( m[2] + 4 ) m[2][4][3] == *( *( m[2] + 4 ) + 3 )
m[2] == *( m + 2 ) m[2][4][3] == *( *( *( m + 2 ) + 4 ) + 3 )
De forma general se tienen las siguientes relaciones:
Matriz de una dimensión: m[a] == *( m + a )
Matriz de 2 dimensiones: m[a][b] == *( *( m + a ) + b )
Matriz de 3 dimensiones: m[a][b][c] == *( *( *( m + a ) + b ) + c )
Matriz de 4 dimensiones: m[a][b][c][d] == *( *( *( *( m + a ) + b ) + c )+ d)
etc.
§4 Operador de llamada a función ( )
En C++ una invocación del tipo func(lista-de-argumentos) es considerada como un operador binario ( 4.9); el denominado operador de invocación a función ( ), que se aplica entre el primer argumento func y el segundo, lista-de-argumentos. En este sentido, la invocación anterior sería equivalente a: func()lista-de-argumentos, aunque en realidad, la sintaxis utilizada sea la primera.
§4.1 Sintaxis
postfix-expression(<arg-expression-list>)
§4.2 Ejemplo
x = somefunc(x, 33, z); // el valor se asigna a x
§4.3 Comentario
El valor (si lo tiene) de la llamada a función es el valor devuelto por esta, tal como se establece en la definición de la función.
Recordar que el paréntesis ( ) puede ser también un elemento de puntuación para agrupar expresiones aritméticas; para aislar expresiones condicionales y para delimitar expresiones con coma.
Este operador puede ser sobrecargado mediante la función-operador operator( ) ( 4.9.18f).
§5 Selector directo de miembro
El selector directo de miembro ( . ) es un operador binario que debe usarse para acceder a los miembros de clases, estructuras y uniones.
§5.1 Sintaxis
expresión . identificador
expresión debe ser un objeto de tipo clase, unión o estructura.
identificador debe ser un miembro de la clase, unión o estructura indicada en expresión.
§5.2 Ejemplo
class CLASE {
int i;
public:
double d;
int geti() { return i; }
} c1 ;
...
c1.d = 3.14; // asigna 3.14 al miembro d del objeto c1
int z = c1.geti(); // invoca el método geti del objeto c1
§5.3 Comentario
Suponiendo que el objeto s sea una instancia de la estructura tipo S. Entonces, si m es el identificador de un miembro de tipo M declarado en S, la expresión s.m es de tipo M y representa al sub-objeto m en s.
Ejemplo:
struct S {...; M m; ...; }; // m es miembro de S tipo M
S s; // s es instancia de S
M x = z;
s.m = x; // asigna x al miembro m de s
Una expresión del tipo sT.obj representa el objeto obj de la estructura sT. (la expresión es un
Lvalue siempre que sT no lo sea y obj no sea una matriz).
Si existen clases o estructuras anidadas, es decir, si una estructura SB contiene un campo que es a su vez una estructura SA, los miembros de la estructura anidada SA pueden ser accedidos mediante una doble aplicación del selector directo de miembro. Ejemplo:
struct SA { int i; char ch; } a;
struct SB { int i; char ch; struct a; } b;
...
b.i = 3; // asigna 3 al miembro i del objeto b
b.ch = 'a'; // asigna 'a' al miembro ch del objeto b
b.a.i = 3; // asigna 3 al miembro i del sub-objeto a de b
b.a.ch = 'a'; // asigna 'a' al miembro ch del sub-objeto a de b
§6 Selector indirecto de miembro
El selector indirecto de miembro ( -> ) es un operador binario que puede utilizarse para acceder a miembros de clases, estructuras y uniones a través de punteros.
Nota: el hecho de acceder a objetos a través de punteros se denomina indirección (de ahí el nombre de este operador), y constituye un recurso de capital importancia y constante uso en programación (comentarios adicionales en 4.9.11).
§6.1 Sintaxis
ClPtr -> identificador
ClPtr debe ser un puntero a clase; puntero a estructura o puntero a unión.
identificador debe ser el nombre de un miembro de la clase, estructura o unión a que señala el puntero.
§6.2 Ejemplo
class CLASE {
public:
double db;
double getd() { return db; }
} c1, *cptr = &c1;
...
cptr->db = 1.23; // asigna 1.23 al miembro db de c1 §6.2a
cptr->getd(); // invoca método getd del objeto c1 §6.2b
cptr->CLASE::getd(); // variación sintáctica de la anterior §6.2c
§6.3 Comentario
Si existen clases o estructuras anidadas, es decir, si la estructura B contiene un campo a de tipo estructura A, los sub-miembros de a pueden ser accedidos mediante una doble aplicación del operador de selección.
Ejemplo:
struct A {
int j; double x;
};
struct B {
int i; double d;
struct A a;
} s, *sptr = &s;
...
sptr->d = 1.2; // asigna 1.2 al miembro d de B
s.d = 1.2; // ídem.
sptr->a.x = 3.1; // asigna 3.1 al miembro x de A
s.a.x = 3.1; // ídem.
Una expresión del tipo ClPtr -> memb representa el miembro memb de la clase Cl siempre que ClPtr sea un puntero a dicha clase (la expresión es un Lvalue siempre que memb no sea una matriz). Esta expresión es equivalente, y preferible a (*clPt).memb
Observe que las expresiones §6.2a, §6.2b y §6.2c pueden ser sustituidas con éxito por la combinación de otros operadores disponibles en el lenguaje, de forma que son equivalentes a:
(*cptr).db = 1.23;
(*cptr).getd();
(*cptr).CLASE::getd();
En realidad la introducción de un operador específico para estos menesteres se debe a que la indirección, a la que nos hemos referido antes, es un elemento clave en programación, importancia que queda subrayada en C++ por la dedicación de este operador específico que simplifica y resalta el mecanismo de acceso cuando se trata de punteros a clases, estructuras y uniones. En estos casos, la notación
cptr->db = 1.23;
es más elegante y concisa que su contrapartida
(*cptr).db = 1.23;
Además, como tendremos ocasión de ver al tratar de la sobrecarga de este operador ( 4.9.18e) la indirección dar lugar a técnicas de programación muy interesantes (a este respecto puede verse también un ejemplo en 4.9.18c).
Este operador puede ser sobrecargado mediante la función-operador operator-> ( 4.9.18e).