Declaración de función dentro o fuera de la clase

91

Soy un desarrollador de JAVA que está tratando de aprender C ++, pero realmente no sé cuál es la mejor práctica para las declaraciones de funciones estándar.

En la clase:

class Clazz
{
 public:
    void Fun1()
    {
        //do something
    }
}

O afuera:

class Clazz
{
public:
    void Fun1();
}

Clazz::Fun1(){
    // Do something
}

Tengo la sensación de que el segundo puede ser menos legible ...

JohnJohnGa
fuente
1
En realidad, hay 3 opciones aquí. Su segundo ejemplo podría tener la definición de la función en el archivo de encabezado (pero aún no en línea), o en un .cpparchivo separado .
Cody Gray
Esta pregunta puede ayudarlo a comprender.
Björn Pollex
3
Solo una nota: la declaración siempre está dentro de la clase, pero la definición está dentro o fuera. El título y el cuerpo de la pregunta deben estar sujetos a s / declaración / definición / ¿No me creen? stackoverflow.com/q/1410563/1143274
Evgeni Sergeev
1
Deben evitarse las definiciones de funciones dentro de la clase. Se consideran implícitamente inline.
John Strood
@JohnStrood ¿así? inlinesolo relaja la regla de una definición, que es necesaria si se usa otra unidad de traducciónClazz
Caleth

Respuestas:

57

C ++ está orientado a objetos, en el sentido de que apoya el paradigma orientado a objetos para el desarrollo de software.

Sin embargo, a diferencia de Java, C ++ no le obliga a agrupar las definiciones de funciones en clases: la forma estándar de C ++ para declarar una función es simplemente declarar una función, sin ninguna clase.

Si, en cambio, está hablando de declaración / definición de método, entonces la forma estándar es poner solo la declaración en un archivo de inclusión (normalmente llamado .ho .hpp) y la definición en un archivo de implementación separado (normalmente llamado .cppo.cxx ). Estoy de acuerdo en que esto es algo molesto y requiere cierta duplicación, pero así es como se diseñó el lenguaje.

Para experimentos rápidos y proyectos de un solo archivo, cualquier cosa funcionaría ... pero para proyectos más grandes, esta separación es algo que se requiere prácticamente.

Nota: Incluso si conoce Java, C ++ es un lenguaje completamente diferente ... y es un lenguaje que no se puede aprender experimentando. La razón es que es un lenguaje bastante complejo con muchas asimetrías y elecciones aparentemente ilógicas, y lo más importante, cuando comete un error, no hay "ángeles de error en tiempo de ejecución" para salvarlo como en Java ... pero en su lugar hay " demonios de comportamiento indefinido ".

La única forma razonable de aprender C ++ es leyendo ... no importa lo inteligente que sea, no hay forma de que pueda adivinar lo que decidió el comité (en realidad, ser inteligente es a veces incluso un problema porque la respuesta correcta es ilógica y una consecuencia de patrimonio.)

Simplemente elija uno o dos libros buenos y léalos de cabo a rabo.

6502
fuente
7
Si alguien viene de Java y pide ayuda sobre C ++, ¿qué le dice si le dices "el idioma que conoces está obsesionado con algo"? No tiene comparación con otros idiomas, así que esto no le dice prácticamente nada. Mejor que usar una palabra fuertemente connotada emocionalmente como obsesionado, que no le dice mucho al OP, podría considerar simplemente omitir esta parte. Además, ¿cuál es el contexto de "usar una clase para todo"? En Java, no usa una clase como método. No usa una clase para una variable. No usas una clase para un archivo. Entonces, ¿qué es "todo" aquí? Despotricando?
Daniel S.
3
@DanielS: Eliminó esa parte porque aparentemente te ofendió (no tengo idea de por qué). Seguro que no estoy despotricando sobre Java porque en realidad no uso Java en absoluto, simplemente pensé en ese momento que la programación orientada a objetos como programación obsesionada con objetos era una broma divertida, mientras que aparentemente no lo es. He sido un programador certificado en Java 1.1, pero decidí en ese entonces que, a menos que sea forzado por alguna razón, no usaré ese "lenguaje de programación" y hasta ahora he logrado evitarlo.
6502
Gracias, creo que ahora se lee mucho mejor. Lo siento si sueno ofendido. Intentaré ser más positivo la próxima vez.
Daniel S.
15
No responde la pregunta
Petr Peller
1
@PetrPeller: ¿cuál es la parte del tercer párrafo que no te queda clara?
6502
27

El primero define su función miembro como una función en línea , mientras que el segundo no lo hace. La definición de la función en este caso reside en el propio encabezado.

La segunda implementación colocaría la definición de la función en el archivo cpp.

Ambos son semánticamente diferentes y no es solo una cuestión de estilo.

Alok Save
fuente
2
cplusplus.com/doc/tutorial/classes da la misma respuesta: "La única diferencia entre definir una función miembro de clase completamente dentro de su clase o incluir solo el prototipo y luego su definición, es que en el primer caso la función será automáticamente considerada una función miembro en línea por el compilador, mientras que en la segunda será una función miembro de clase normal (no en línea), que de hecho supone que no hay diferencia en el comportamiento ".
Buttons840
18

La definición de funciones es mejor fuera de la clase. De esa manera, su código puede permanecer seguro si es necesario. El archivo de encabezado solo debe dar declaraciones.

Suponga que alguien quiere usar su código, puede simplemente darle el archivo .hy el archivo .obj (obtenido después de la compilación) de su clase. No necesita el archivo .cpp para usar su código.

De esa manera, su implementación no será visible para nadie más.

Ajit Vaze
fuente
10

El método "Dentro de la clase" (I) hace lo mismo que el método "Fuera de la clase" (O).

Sin embargo, (I) se puede usar cuando una clase solo se usa en un archivo (dentro de un archivo .cpp). (O) se usa cuando está en un archivo de encabezado. Los archivos cpp siempre se compilan. Los archivos de encabezado se compilan cuando usa #include "header.h".

Si usa (I) en un archivo de encabezado, la función (Fun1) se declarará cada vez que incluya #include "header.h". Esto puede llevar a declarar la misma función varias veces. Esto es más difícil de compilar e incluso puede provocar errores.

Ejemplo de uso correcto:

Archivo1: "Clazz.h"

//This file sets up the class with a prototype body. 

class Clazz
{
public:
    void Fun1();//This is a Fun1 Prototype. 
};

Archivo2: "Clazz.cpp"

#include "Clazz.h" 
//this file gives Fun1() (prototyped in the header) a body once.

void Clazz::Fun1()
{
    //Do stuff...
}

Archivo3: "UseClazz.cpp"

#include "Clazz.h" 
//This file uses Fun1() but does not care where Fun1 was given a body. 

class MyClazz;
MyClazz.Fun1();//This does Fun1, as prototyped in the header.

Archivo4: "AlsoUseClazz.cpp"

#include "Clazz.h" 
//This file uses Fun1() but does not care where Fun1 was given a body. 

class MyClazz2;
MyClazz2.Fun1();//This does Fun1, as prototyped in the header. 

Archivo5: "DoNotUseClazzHeader.cpp"

//here we do not include Clazz.h. So this is another scope. 
class Clazz
{
public:
    void Fun1()
    {
         //Do something else...
    }
};

class MyClazz; //this is a totally different thing. 
MyClazz.Fun1(); //this does something else. 
Maarten t Hart
fuente
¿Te refieres a Clazz MyClazzy Clazz MyClazz2?
Chupo_cro
4

Las funciones miembro se pueden definir dentro de la definición de clase o por separado usando el operador de resolución de alcance, ::. La definición de una función miembro dentro de la definición de clase declara la función en línea, incluso si no usa el especificador en línea. Entonces, puede definir la función Volume () de la siguiente manera:

class Box
{
  public:

     double length;
     double breadth;    
     double height;     

     double getVolume(void)
     {
        return length * breadth * height;
     }
};

Si lo desea, puede definir la misma función fuera de la clase utilizando el operador de resolución de alcance, :: de la siguiente manera

double Box::getVolume(void)
{
   return length * breadth * height;
}

Aquí, el único punto importante es que tendría que usar el nombre de la clase justo antes de :: operator. Se llamará a una función miembro usando un operador de punto (.) En un objeto donde manipulará los datos relacionados con ese objeto solo de la siguiente manera:

Box myBox;           

myBox.getVolume();  

(de: http://www.tutorialspoint.com/cplusplus/cpp_class_member_functions.htm ), ambas formas son legales.

No soy un experto, pero creo que si pones solo una definición de clase en un archivo, entonces realmente no importa.

pero si aplica algo como clase interna, o tiene una definición de clase múltiple, la segunda sería difícil de leer y mantener.

usuario116541
fuente
1
¿Puede traer el contenido relevante de ese enlace al cuerpo de su publicación y, por lo tanto, a prueba de futuro contra los enlaces muertos? Gracias
JustinJDavies
2

El primero debe colocarse en el archivo de encabezado (donde reside la declaración de la clase). El segundo puede estar en cualquier lugar, ya sea el encabezado o, por lo general, un archivo fuente. En la práctica, puede poner pequeñas funciones en la declaración de la clase (que las declara implícitamente en línea, aunque es el compilador el que finalmente decide si estarán en línea o no). Sin embargo, la mayoría de las funciones tienen una declaración en el encabezado y la implementación en un archivo cpp, como en su segundo ejemplo. Y no, no veo ninguna razón por la que esto sea menos legible. Sin mencionar que podría dividir la implementación para un tipo en varios archivos cpp.

Marius Bancila
fuente
1

Una función que se define dentro de una clase se trata de forma predeterminada como una función en línea. Una simple razón por la que debería definir su función fuera:

Un constructor de la clase busca funciones virtuales e inicializa un puntero virtual para apuntar al VTABLE apropiado o la tabla del método virtual , llama al constructor de la clase base e inicializa las variables de la clase actual, por lo que realmente funciona.

Las funciones en línea se utilizan cuando las funciones no son tan complicadas y evita la sobrecarga de la llamada a la función. (La sobrecarga incluye un salto y una ramificación en el nivel de hardware). Y como se describió anteriormente, el constructor no es tan simple de considerar como en línea.

R Mehta
fuente
"inline" no tiene prácticamente nada que ver con inlining. El hecho de que las funciones miembro definidas en línea se declaren implícitamente en línea está ahí para evitar violaciones de ODR.
Big Temp
0

Funciones en línea (funciones cuando las declaras en la clase) cada vez que las llamas se pegan en tu código de memoria principal. Mientras que cuando declaras la función fuera de la clase, cuando llamas a la función, proviene de la misma memoria. Por eso es mucho mejor.

elegante
fuente