Non-static member functions
Una función miembro no estática es una función que se declara en una
especificación de miembro
de una clase sin un especificador
static
o
friend
(ver
funciones miembro estáticas
y
declaración friend
para el efecto de esas palabras clave).
class S { int mf1(); // declaración de función miembro no estática void mf2() volatile, mf3() &&; // puede tener calificadores cv y/o un calificador de referencia // la declaración anterior es equivalente a dos declaraciones separadas: // void mf2() volatile; // void mf3() &&; int mf4() const { return data; } // puede definirse en línea virtual void mf5() final; // puede ser virtual, puede usar final/override S() : data(12) {} // los constructores también son funciones miembro int data; }; int S::mf1() { return 7; } // si no se define en línea, debe definirse en el espacio de nombres
Constructores , destructores , y funciones de conversión utilizan sintaxis especiales para sus declaraciones. Las reglas descritas en esta página pueden no aplicarse a estas funciones. Consulte sus respectivas páginas para más detalles.
|
Una función miembro de objeto explícito es una función miembro no estática con un parámetro de objeto explícito . |
(since C++23) |
Una función miembro de objeto implícita es una función miembro no estática sin un parámetro de objeto explícito (antes de C++23, este era el único tipo de función miembro no estática, y por lo tanto se refería como "función miembro no estática" en la literatura).
Contenidos |
Explicación
Se permiten
declaraciones de funciones
, con elementos de sintaxis adicionales que solo están disponibles para funciones miembro no estáticas:
especificadores puros
, calificadores cv
, calificadores de referencia,
final
y
override
especificadores
(desde C++11)
, y
listas de inicialización de miembros
.
Una función miembro no estática de la clase
X
puede ser llamada
X
utilizando el operador de acceso a miembro de clase
X
X
Llamar a una función miembro no estática de la clase
X
sobre un objeto que no es de tipo
X
, o de un tipo derivado de
X
provoca comportamiento indefinido.
Dentro del cuerpo de una función miembro no estática de
X
, cualquier
expresión de identificador
e
(por ejemplo, un identificador) que se resuelva en un miembro no estático de tipo no-clase de
X
o de una clase base de
X
, se transforma en una expresión de acceso a miembro
(
*
this
)
.
e
(a menos que ya forme parte de una expresión de acceso a miembro). Esto no ocurre en contexto de definición de plantilla, por lo que un nombre puede necesitar ser prefijado con
this
-
>
explícitamente para volverse
dependiente
.
struct S { int n; void f(); }; void S::f() { n = 1; // transformado a (*this).n = 1; } int main() { S s1, s2; s1.f(); // modifica s1.n }
Dentro del cuerpo de una función miembro no estática de
X
, cualquier identificador no calificado que se resuelva en un miembro estático, un enumerador o un tipo anidado de
X
o de una clase base de
X
, se transforma en el identificador calificado correspondiente:
struct S { static int n; void f(); }; void S::f() { n = 1; // transformado a S::n = 1; } int main() { S s1, s2; s1.f(); // cambia S::n }
Funciones miembro con calificadores cv
Una función miembro de objeto implícita puede declararse con una secuencia de calificadores cv ( const , volatile , o una combinación de const y volatile ), esta secuencia aparece después de la lista de parámetros en la declaración de función . Las funciones con diferentes secuencias de calificadores cv (o sin secuencia) tienen tipos diferentes y por lo tanto pueden sobrecargarse entre sí.
En el cuerpo de una función con una secuencia de calificadores cv,
*
this
está calificado cv, por ejemplo, en una función miembro con calificador
const
, solo se pueden llamar normalmente otras funciones miembro con calificador
const
. Una función miembro sin calificador
const
aún puede ser llamada si se aplica
const_cast
o a través de una ruta de acceso que no involucre
this
.
#include <vector> struct Array { std::vector<int> data; Array(int sz) : data(sz) {} // función miembro const int operator[](int idx) const { // el puntero this tiene tipo const Array* return data[idx]; // transformado a (*this).data[idx]; } // función miembro no const int& operator[](int idx) { // el puntero this tiene tipo Array* return data[idx]; // transformado a (*this).data[idx] } }; int main() { Array a(10); a[1] = 1; // OK: el tipo de a[1] es int& const Array ca(10); ca[1] = 2; // Error: el tipo de ca[1] es int }
Funciones miembro con calificador de referencia
Una función miembro de objeto implícito puede declararse sin calificador de referencia, con calificador de referencia de lvalue (el token
Nota: a diferencia de la calificación cv, la calificación de referencia no cambia las propiedades del puntero
|
(desde C++11) |
Funciones virtuales y puras virtuales
Una función miembro no estática puede declararse virtual o pura virtual . Consulte funciones virtuales y clases abstractas para más detalles.
Funciones miembro de objeto explícitoPara una función miembro no estática no virtual no declarada con calificador cv o calificador de referencia, su primer parámetro, si no es un paquete de parámetros de función , puede ser un parámetro de objeto explícito (denotado con la palabra clave prefijada this ): struct X { void foo(this X const& self, int i); // same as void foo(int i) const &; // void foo(int i) const &; // Error: already declared void bar(this X self, int i); // pass object by value: makes a copy of “*this” }; Para plantillas de funciones miembro, el parámetro de objeto explícito permite la deducción del tipo y categoría de valor, esta característica del lenguaje se denomina "deducción de this ": struct X { template<typename Self> void foo(this Self&&, int); }; struct D : X {}; void ex(X& x, D& d) { x.foo(1); // Self = X& move(x).foo(2); // Self = X d.foo(3); // Self = D& } Esto hace posible eliminar la duplicación de funciones miembro const y no const, consulte operador de subíndice de array para ver un ejemplo. Dentro del cuerpo de una función miembro de objeto explícito, el puntero this no puede utilizarse: todo acceso a miembros debe realizarse a través del primer parámetro, como en las funciones miembro estáticas: struct C { void bar(); void foo(this C c) { auto x = this; // error: no this bar(); // error: no implicit this-> c.bar(); // ok } }; Un puntero a una función miembro de objeto explícito es un puntero ordinario a función, no un puntero a miembro: struct Y { int f(int, int) const&; int g(this Y const&, int, int); }; auto pf = &Y::f; pf(y, 1, 2); // error: pointers to member functions are not callable (y.*pf)(1, 2); // ok std::invoke(pf, y, 1, 2); // ok auto pg = &Y::g; pg(y, 3, 4); // ok (y.*pg)(3, 4); // error: “pg” is not a pointer to member function std::invoke(pg, y, 3, 4); // ok |
(desde C++23) |
Funciones miembro especiales
Algunas funciones miembro son especiales : bajo ciertas circunstancias son definidas por el compilador incluso si no son definidas por el usuario. Son:
| (desde C++11) |
| (desde C++11) |
- Destructor (hasta C++20) Prospective destructor (desde C++20)
Funciones miembro especiales junto con los operadores de comparación (desde C++20) son las únicas funciones que pueden ser defaulted , es decir, definidas usando = default en lugar del cuerpo de la función (consulte sus páginas para más detalles).
Notas
| Macro de prueba de características | Valor | Estándar | Característica |
|---|---|---|---|
__cpp_ref_qualifiers
|
200710L
|
(C++11) | calificadores de referencia |
__cpp_explicit_this_parameter
|
202110L
|
(C++23) |
parámetro de objeto explícito
(
deducción de
this
)
|
Ejemplo
#include <exception> #include <iostream> #include <string> #include <utility> struct S { int data; // constructor de conversión simple (declaración) S(int val); // constructor explícito simple (declaración) explicit S(std::string str); // función miembro constante (definición) virtual int getData() const { return data; } }; // definición del constructor S::S(int val) : data(val) { std::cout << "ctor1 llamado, data = " << data << '\n'; } // este constructor tiene una cláusula catch S::S(std::string str) try : data(std::stoi(str)) { std::cout << "ctor2 llamado, data = " << data << '\n'; } catch(const std::exception&) { std::cout << "ctor2 falló, string era '" << str << "'\n"; throw; // la cláusula catch del constructor siempre debe relanzar } struct D : S { int data2; // constructor con un argumento por defecto D(int v1, int v2 = 11) : S(v1), data2(v2) {} // función miembro virtual int getData() const override { return data * data2; } // operador de asignación solo para lvalues D& operator=(D other) & { std::swap(other.data, data); std::swap(other.data2, data2); return *this; } }; int main() { D d1 = 1; S s2("2"); try { S s3("not a number"); } catch(const std::exception&) {} std::cout << s2.getData() << '\n'; D d2(3, 4); d2 = d1; // OK: asignación a lvalue // D(5) = d1; // ERROR: no hay sobrecarga adecuada de operator= }
Salida:
ctor1 llamado, data = 1 ctor2 llamado, data = 2 ctor2 falló, string era 'not a number' 2 ctor1 llamado, data = 3
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 194 | C++98 |
ambiguo si una función miembro no estática
podría tener el mismo nombre que el nombre de la clase contenedora |
se añadió restricción de nomenclatura explícita |