Separando o código de classe C ++ em vários arquivos, quais são as regras?

Tempo de Pensamento - Por que você quer dividir seu arquivo de qualquer maneira?

Como o título sugere, o problema final que eu tenho é erros de linker de múltipla definição. Eu realmente consertei o problema, mas não consertei o problema da maneira correta. Antes de começar, quero discutir as razões para dividir um arquivo de classe em vários arquivos. Eu tentei colocar todos os cenários possíveis aqui - se eu perdi algum, por favor, lembre-me e eu posso fazer alterações. Espero que o seguinte esteja correto:

Razão 1 Para economizar espaço:

Você tem um arquivo contendo a declaração de uma classe com todos os alunos. Você coloca guardas #include em torno desse arquivo (ou #pragma uma vez) para garantir que não haja conflitos se você #incluir o arquivo em dois arquivos de cabeçalho diferentes que serão incluídos em um arquivo de origem. Você compila um arquivo fonte separado com a implementação de qualquer método declarado nesta classe, já que ele carrega muitas linhas de código do seu arquivo de origem, o que limpa um pouco as coisas e introduz alguma ordem no seu programa.

Exemplo:Como você pode ver, o exemplo abaixo poderia ser melhorado dividindo a implementação dos métodos de classe em um arquivo diferente. (Um arquivo .cpp)

// my_class.hpp
#pragma once

class my_class
{
public:
    void my_function()
    {
        // LOTS OF CODE
        // CONFUSING TO DEBUG
        // LOTS OF CODE
        // DISORGANIZED AND DISTRACTING
        // LOTS OF CODE
        // LOOKS HORRIBLE
        // LOTS OF CODE
        // VERY MESSY
        // LOTS OF CODE
    }

    // MANY OTHER METHODS
    // MEANS VERY LARGE FILE WITH LOTS OF LINES OF CODE
}

Razão 2 Para evitar erros do link de definição múltipla:

Talvez esta seja a principal razão pela qual você dividiria a implementação da declaração. No exemplo acima, você poderia mover o corpo do método para fora da classe. Isso faria com que parecesse muito mais limpo e estruturado. No entanto, de acordo com estequestão, o exemplo acima tem implícitoinline especificadores. Mover a implementação de dentro da classe para fora da classe, como no exemplo abaixo, causará erros de vinculação e, portanto, você poderá inline tudo ou mover as definições de função para um arquivo .cpp.

Exemplo: _O exemplo abaixo causará "erros de link de definição múltipla" se você não mover a definição da função para um arquivo .cpp ou especificar a função como inline.

// my_class.hpp
void my_class::my_function()
{
    // ERROR! MULTIPLE DEFINITION OF my_class::my_function
    // This error only occurs if you #include the file containing this code
    // in two or more separate source (compiled, .cpp) files.
}

Para consertar o problema:

//my_class.cpp
void my_class::my_function()
{
    // Now in a .cpp file, so no multiple definition error
}

Ou:

// my_class.hpp
inline void my_class::my_function()
{
    // Specified function as inline, so okay - note: back in header file!
    // The very first example has an implicit `inline` specifier
}

Razão 3 Você quer economizar espaço novamente, mas desta vez você está trabalhando com uma classe de modelo:

Se estivermos trabalhando com classes de modelo, não poderemos mover a implementação para um arquivo de origem (arquivo .cpp). Isso não é permitido atualmente (suponho) pelo padrão ou pelos compiladores atuais. Ao contrário do primeiro exemplo deRazão 2acima, temos permissão para colocar a implementação no arquivo de cabeçalho. De acordo com issoquestão a razão é que os métodos de classe de modelo também implicaraminline especificadores. Isso está correto? (Parece fazer sentido.) Mas ninguém parecia saber sobre a questão que acabei de referenciar!

Então, os dois exemplos abaixo são idênticos?

// some_header_file.hpp
#pragma once

// template class declaration goes here
class some_class
{
    // Some code
};

// Example 1: NO INLINE SPECIFIER
template<typename T>
void some_class::class_method()
{
    // Some code
}

// Example 2: INLINE specifier used
template<typename T>
inline void some_class::class_method()
{
    // Some code
}

Se você tem um arquivo de cabeçalho de classe de modelo, que está se tornando enorme devido a todas as funções que você tem, então acredito que você tem permissão para mover as definições de função para outro arquivo de cabeçalho (normalmente um arquivo .tpp) e#include file.tpp no final do seu arquivo de cabeçalho contendo a declaração da classe. Você NÃO deve incluir este arquivo em nenhum outro lugar, entretanto,.tpp ao invés de.hpp.

Eu suponho que você também poderia fazer isso com os métodos inline de uma classe regular? Isso é permitido também?

Questão de tempo

Por isso, fiz algumas declarações acima, a maioria das quais relacionadas à estruturação de arquivos de origem. Acho que tudo o que eu disse estava correto, porque fiz uma pesquisa básica e "descobri algumas coisas", mas isso é uma questão e, por isso, não tenho certeza.

O que isso significa é como você organizaria o código nos arquivos. Acho que descobri uma estrutura que sempre funcionará.

Aqui está o que eu inventei. (Isto é"Organização do arquivo de código de classe do Ed Bird / estrutura padrão", se você gostar. Não sei se será muito útil ainda, esse é o ponto de perguntar.)

1: Declare a classe (modelo ou de outra forma) em um.hpp arquivo, incluindo todos os métodos, funções de amigos e dados.2: Na parte inferior do.hpp Arquivo,#include a .tpp arquivo contendo a implementação de qualquerinline métodos. Crie o.tpp arquivo e garantir que todos os métodos são especificados para serinline.3: Todos os outros membros (funções não-inline, funções friend e dados estáticos) devem ser definidos.cpp arquivo, que#includes o.hpp arquivo no topo para evitar erros como "classe ABC não foi declarada". Como tudo neste arquivo terá ligação externa, o programa será vinculado corretamente.

Padrões como esse existem na indústria? Será que o padrão que eu criei funcionaria em todos os casos?

questionAnswers(3)

yourAnswerToTheQuestion