c++ - que - ¿Por qué están en línea las funciones de los miembros de la clase?
programacion orientada a objetos ejemplos c++ (6)
Creo que mi pregunta ha sido formulada aquí anteriormente, las leí, pero todavía estaba un poco confundida y, por lo tanto, pedí que se aclarara.
The C++ standard says all member functions defined inside class definition are inline
También he oído que el compilador puede ignorar la incorporación de una función. ¿Será eso cierto en el caso anterior o estará siempre en línea si se define dentro de la definición de clase?
Además, ¿cuál fue la razón detrás de este diseño, haciendo que todas las funciones definidas dentro de la definición de clase estén en línea? ¿Y qué tiene que ver la alineación con los archivos de origen y de encabezado?
Actualización: Por lo tanto, siempre se deben definir sus funciones fuera de la clase, para no estar en línea, ¿verdad?
Actualización 2 por JohnB: Dos funciones declaradas dentro de la definición de clase nunca podrían llamarse entre sí, ya que tendrían que contener el cuerpo completo de la otra función. ¿Qué pasará en este caso? (Ya contestado por Emilio Garavaglia)
Cuando la definición está dentro de la clase, se trata como si se hubiera declarado en inline
, porque se supone que las definiciones de clase viven en archivos de encabezado que se utilizan desde más de una unidad de traducción, por lo que cualquier definición que no esté en línea violaría la Regla de definición.
El compilador es, como siempre, libre de en línea lo que sea que piense, siempre que cuide que las funciones que están en línea de forma explícita o implícita no den lugar a errores de vinculador. Cómo lo hace, queda abierto por la especificación de idioma: la función de alineación de la función funciona por supuesto, pero también es aceptable degradar la visibilidad del símbolo o cambiar el nombre del símbolo a un nombre específico de la unidad de traducción (como si la función estuviera en un espacio de nombres anónimo) ), o (como la mayoría de ellos) comunican al vinculador que pueden existir múltiples copias de esa función y que deben descartar todas menos una de ellas.
Entonces, en resumen, no se trata de manera diferente de las funciones que se declaran explícitamente en inline
.
El compilador puede ignorar la inclusión en línea si está especificado por la palabra clave en inline
. Si la implementación del método está presente dentro de la definición de la clase, eso es algo diferente y no se puede ignorar. (bueno, puede, pero eso hace que el compilador no sea conforme)
La razón detrás del diseño: supongo que se necesitaba un mecanismo donde realmente se puede forzar al compilador a que realmente integre sus funciones, ya que la palabra clave en inline
no lo exige. Pero en general, la definición del método en línea se realiza solo en casos como los métodos de obtención y establecimiento, o algunos triviales 2-liners. Y las plantillas, pero eso es un tema diferente.
La alineación tiene que ver con los encabezados y los archivos de origen, ya que la definición de la función debe ser visible para el compilador para que sepa cómo realmente alinear la llamada. Es más difícil alinear una función definida en un archivo de implementación que una definida en un encabezado.
EDITAR: En una nota lateral, el párrafo al que se 7.1.2.3
es 7.1.2.3
:
Una función definida dentro de una definición de clase es una función en línea [...].
EDIT2:
Aparentemente, hay alguna diferencia entre una función en línea y una sustitución en línea. La primera es una propiedad de una función, que no solo incluye la sustitución en línea, la segunda significa que el cuerpo de la función está realmente pegado donde se llama.
Por lo tanto, la función puede estar en línea pero no tener su cuerpo pegado en lugar de ser llamado.
La única razón para hacer que el método funcione en línea es si lo define en el encabezado.
Si define una función de método en un encabezado y no coloca una palabra clave en línea, e incluye el encabezado en varios encabezados o archivos de origen, obtendrá una definición múltiple del método.
El estándar c ++ 11 en 9.3 / 2 Funciones miembro [class.mfct] dice:
Una función miembro se puede de fi nir (8.4) en su definición de clase, en cuyo caso es una función miembro en línea (7.1.2) ...
La confusión surge porque la línea tiene dos efectos:
- Le dice al compilador que el código de la función se puede expandir donde se llama a la función, en lugar de llamarlo efectivamente.
- Le dice al compilador que la definición de la función se puede repetir.
El punto 1. es "arcaico" en el sentido de que el compilador puede, de hecho, hacer lo que quiera para optimizar el código. Siempre podrá "en línea" el código de la máquina si puede y lo encontrará conveniente y nunca lo hará si no puede.
El punto 2 es el significado real del término: si define
(especifique el cuerpo) una función en el encabezado, ya que un encabezado se puede incluir en más fuentes, debe decirle al compilador que informe al vinculador sobre los duplicados de la definición, por lo que que pueden ser fusionados.
Ahora, según la especificación del lenguaje, las funciones libres (no definidas en cuerpos de clase) no están definidas por defecto como en línea, por lo que definir en un encabezado una cosa como
void myfunc()
{}
Si el encabezado se incluye en más fuentes y luego se vincula en una misma salida, el vinculador notificará un error de definición múltiple, de ahí la necesidad de definirlo como
inline void fn()
{}
Para los miembros de la clase, el valor predeterminado es el opuesto: si los declara, no estarán en línea. Si los define, estarán en línea.
Así que un encabezado debería verse como
//header file
class myclass
{
public:
void fn1()
{} //defined into the class, so inlined by default
void fn2();
};
inline void myclass::fn2()
{} //defined outside the class, so explicit inline is needed
Y si myclass::fn2()
definición entra en una fuente adecuada, debe perder la palabra clave en inline
.
La palabra clave en inline
tiene para una función 2 significados:
- Reemplazo de código : dondequiera que se invoque la función en
inline
, no genere una llamada a la función sino que simplemente coloque el contenido de la función en el lugar de su llamada (esto es algo similar a la sustitución de macros, pero es seguro). - Una regla de definición : no genere múltiples definiciones para una función en
inline
, solo genere una única definición común para todas (excepción: funcionesstatic
)
La primera terminología ("Reemplazo de código") es simplemente una solicitud al compilador. lo que se puede ignorar como compilador es mejor juzgar si se debe poner el texto o una llamada de función. (por ejemplo, virtual
funciones virtual
o las funciones recursivas no pueden estar en línea).
Se garantiza que la segunda terminología ("regla de una definición") sucederá con cualquier compilador conforme. Esto generará solo 1 definición para todas las unidades de traducción. Esta facilidad facilita el trabajo del codificador a veces, ya que para una función más pequeña es posible que no quiera poner su definición en el archivo .cpp
(por ejemplo, getters, setters).
Además, para template
función de template
que son construcciones de encabezado solamente, este efecto es obligatorio. Por lo tanto, template
funciones de la template
están en inline
por defecto.
Ejemplos:
class A {
public:
void setMember (int i) { m_i = i; }
};
En este ejemplo la mayoría del compilador bastaría con ambas terminologías
class A {
inline virtual ~A () = 0;
};
A::~A() {}
Aquí el compilador solo puede cumplir el segundo requisito.
Las dos cosas a las que te refieres son aspectos diferentes y no debes confundirlas.
1) El estándar de C ++ dice que todas las funciones miembro definidas dentro de la definición de clase están en línea
2) También he oído que el compilador puede ignorar la incorporación de una función
1) es cuando define las funciones miembro dentro de la declaración de clase en sí. Es decir: en los archivos de cabecera. para eso no tiene que proporcionar ninguna palabra clave (es decir, en inline
)
2) Puede especificar una función como en línea utilizando explícitamente la palabra clave en inline
. Esto es en realidad una solicitud al compilador. el compilador puede o no hacer la función en línea de acuerdo con algunas reglas de optimización.