Declaração de função dentro ou fora da classe

90

Sou um desenvolvedor JAVA que está tentando aprender C ++, mas não sei realmente qual é a prática recomendada para declarações de função padrão.

Na aula:

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

Ou fora:

class Clazz
{
public:
    void Fun1();
}

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

Tenho a sensação de que o segundo pode ser menos legível ...

JohnJohnGa
fonte
1
Na verdade, existem 3 opções aqui. Seu segundo exemplo poderia ter a definição da função no arquivo de cabeçalho (mas ainda não embutido) ou em um .cpparquivo separado .
Cody Gray
Esta pergunta pode ajudá-lo a entender.
Björn Pollex
3
Apenas uma observação: a declaração está sempre dentro da classe, mas a definição está dentro ou fora. O título e o corpo da pergunta devem estar sujeitos a s / declaração / definição / Não acredita em mim? stackoverflow.com/q/1410563/1143274
Evgeni Sergeev
1
Definições de funções dentro da classe devem ser evitadas. Eles são considerados implicitamente inline.
John Strood de
@JohnStrood, então? inlineapenas relaxa a regra de uma definição, que é necessária se outra unidade de tradução usarClazz
Caleth

Respostas:

57

C ++ é orientado a objetos, no sentido de que suporta o paradigma orientado a objetos para o desenvolvimento de software.

No entanto, diferentemente de Java, C ++ não força você a agrupar definições de função em classes: a maneira padrão do C ++ para declarar uma função é apenas declarar uma função, sem nenhuma classe.

Se, em vez disso, você estiver falando sobre declaração / definição de método, a maneira padrão é colocar apenas a declaração em um arquivo de inclusão (normalmente denominado .hou .hpp) e a definição em um arquivo de implementação separado (normalmente denominado .cppou .cxx). Eu concordo que isso é realmente um pouco chato e requer algumas duplicações, mas é como a linguagem foi projetada.

Para experimentos rápidos e projetos de arquivo único, qualquer coisa funcionaria ... mas para projetos maiores, essa separação é praticamente necessária.

Nota: Mesmo se você conhece Java, C ++ é uma linguagem completamente diferente ... e é uma linguagem que não pode ser aprendida experimentando. A razão é que é uma linguagem bastante complexa com muitas assimetrias e escolhas aparentemente ilógicas e, o mais importante, quando você comete um erro, não há "anjos de erro em tempo de execução" para salvá-lo como em Java ... mas há " daemons de comportamento indefinido ".

A única maneira razoável de aprender C ++ é lendo ... não importa o quão inteligente você seja, não há como você adivinhar o que o comitê decidiu (na verdade, ser inteligente às vezes é até um problema porque a resposta correta é ilógica e uma consequência do histórico herança.)

Escolha um ou dois bons livros e leia-os de capa a capa.

6502
fonte
7
Se alguém vem de Java e pede ajuda em C ++, o que isso diria a ele se você dissesse "a linguagem que você conhece é obcecada por alguma coisa"? Ele não tem uma comparação com outras línguas, então isso não lhe diz quase nada. Melhor do que usar uma palavra fortemente conotada emocionalmente como obcecado, o que não diz muito ao OP, você pode considerar apenas deixar essa parte de fora. Além disso, qual é o contexto de "usar uma classe para tudo"? Em Java, você não usa uma classe para um método. Você não usa uma classe para uma variável. Você não usa uma classe para um arquivo ... Então, o que é "tudo" aqui? Ranting?
Daniel S.
3
@DanielS: Removido essa parte porque aparentemente te ofendeu (não tenho ideia do porquê). Com certeza não estou reclamando do Java porque, na verdade, não uso o Java, simplesmente pensei na época que OOP como Object Obsessed Programming era uma piada engraçada, embora aparentemente não seja. Eu fui um programador certificado em Java 1.1, mas decidi naquela época que, a menos que forçado por algum motivo, não usarei essa "linguagem de programação" e até agora consegui evitá-la.
6502 de
Obrigado, acho que está muito melhor agora. Desculpe se pareço ofendido. Vou tentar ser mais positivo da próxima vez.
Daniel S.
13
Não responde à pergunta
Petr Peller
1
@PetrPeller: qual é a parte do terceiro parágrafo que não está clara para você?
6502
26

O primeiro define sua função de membro como uma função embutida , enquanto a segunda não. A definição da função, neste caso, reside no próprio cabeçalho.

A segunda implementação colocaria a definição da função no arquivo cpp.

Ambos são semanticamente diferentes e não é apenas uma questão de estilo.

Alok Save
fonte
2
cplusplus.com/doc/tutorial/classes dá a mesma resposta: "A única diferença entre definir uma função membro de classe completamente dentro de sua classe ou incluir apenas o protótipo e posteriormente sua definição, é que no primeiro caso a função será automaticamente considerada uma função de membro embutida pelo compilador, enquanto na segunda será uma função de membro de classe normal (não embutida), que de fato não supõe nenhuma diferença no comportamento. "
Buttons840
18

A definição da função é melhor fora da classe. Dessa forma, seu código pode permanecer seguro, se necessário. O arquivo de cabeçalho deve apenas fornecer declarações.

Suponha que alguém queira usar seu código, você pode simplesmente fornecer a ele o arquivo .h e o arquivo .obj (obtido após a compilação) de sua classe. Ele não precisa do arquivo .cpp para usar seu código.

Dessa forma, sua implementação não ficará visível para ninguém.

Ajit Vaze
fonte
10

O método "Dentro da classe" (I) faz o mesmo que o método "fora da classe" (O).

No entanto, (I) pode ser usado quando uma classe é usada apenas em um arquivo (dentro de um arquivo .cpp). (O) é usado quando está em um arquivo de cabeçalho. Os arquivos cpp são sempre compilados. Arquivos de cabeçalho são compilados quando você usa #include "header.h".

Se você usar (I) em um arquivo de cabeçalho, a função (Fun1) será declarada toda vez que você incluir #include "header.h". Isso pode levar à declaração da mesma função várias vezes. Isso é mais difícil de compilar e pode até levar a erros.

Exemplo para uso correto:

Arquivo1: "Clazz.h"

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

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

Arquivo2: "Clazz.cpp"

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

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

Arquivo 3: "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.

Arquivo 4: "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. 

Arquivo 5: "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
fonte
3

As funções de membro podem ser definidas dentro da definição de classe ou separadamente usando o operador de resolução de escopo, ::. Definir uma função de membro dentro da definição de classe declara a função embutida, mesmo se você não usar o especificador embutido. Portanto, você pode definir a função Volume () conforme abaixo:

class Box
{
  public:

     double length;
     double breadth;    
     double height;     

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

Se desejar, você pode definir a mesma função fora da classe usando o operador de resolução de escopo, :: como segue

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

Aqui, o único ponto importante é que você teria que usar o nome da classe antes de :: operador. Uma função de membro será chamada usando um operador ponto (.) Em um objeto onde manipulará dados relacionados a esse objeto apenas da seguinte maneira:

Box myBox;           

myBox.getVolume();  

(em: http://www.tutorialspoint.com/cplusplus/cpp_class_member_functions.htm ), ambas as formas são legais.

Não sou um especialista, mas acho que, se você colocar apenas uma definição de classe em um arquivo, isso realmente não importa.

mas se você aplicar algo como classe interna, ou tiver várias definições de classe, a segunda será difícil de ler e manter.

user116541
fonte
1
Você pode trazer o conteúdo relevante desse link para o corpo da sua postagem e, portanto, à prova de futuro contra links inativos? Obrigado
JustinJDavies
2

O primeiro deve ser colocado no arquivo de cabeçalho (onde reside a declaração da classe). O segundo pode estar em qualquer lugar, seja no cabeçalho ou, normalmente, em um arquivo de origem. Na prática, você pode colocar pequenas funções na declaração da classe (que as declara implicitamente inline, embora seja o compilador que decide se elas serão inline ou não). No entanto, a maioria das funções tem uma declaração no cabeçalho e a implementação em um arquivo cpp, como em seu segundo exemplo. E não, não vejo nenhuma razão para que isso seja menos legível. Sem mencionar que você pode realmente dividir a implementação de um tipo em vários arquivos cpp.

Marius Bancila
fonte
1

Uma função definida dentro de uma classe é tratada por padrão como uma função embutida. Uma razão simples pela qual você deve definir sua função externamente:

Um construtor da classe verifica as funções virtuais e inicializa um ponteiro virtual para apontar para a VTABLE adequada ou a tabela de método virtual , chama o construtor da classe base e inicializa as variáveis ​​da classe atual, de modo que realmente faz algum trabalho.

As funções inline são usadas quando as funções não são tão complicadas e evitam a sobrecarga da chamada da função. (A sobrecarga inclui um salto e uma ramificação no nível do hardware.) E, conforme descrito acima, o construtor não é tão simples de ser considerado como embutido.

R Mehta
fonte