Pergunta

É boa prática ter um construtor de classe que utiliza parâmetros padrão, ou devo usar separado construtores sobrecarregados?Por exemplo:

// Use this...
class foo  
{
private:
    std::string name_;
    unsigned int age_;
public:
    foo(const std::string& name = "", const unsigned int age = 0) :
        name_(name),
        age_(age)
    {
        ...
    }
};

// Or this?
class foo  
{
private:
    std::string name_;
    unsigned int age_;
public:
    foo() :
    name_(""),
    age_(0)
{
}

foo(const std::string& name, const unsigned int age) :
        name_(name),
        age_(age)
    {
        ...
    }
};

Tanto a versão parece funcionar, por exemplo:

foo f1;
foo f2("Name", 30);

Qual estilo você prefere ou recomendar e por quê?

Foi útil?

Solução

Definitivamente uma questão de estilo. Prefiro construtores com parâmetros padrão, desde que os parâmetros façam sentido. As aulas no padrão também as usam, o que fala a seu favor.

Uma coisa a observar é que, se você tiver padrões para todos, exceto um parâmetro, sua classe pode ser implicitamente convertida desse tipo de parâmetro. Verificação de saída este tópico para mais informações.

Outras dicas

Eu iria com os argumentos padrão, especialmente porque o C ++ não permite que você faça construtores de cadeia (então você acaba tendo que duplicar a lista de iniciadores e, possivelmente, mais para cada sobrecarga).

Dito isto, existem alguns sugestões com argumentos padrão, incluindo o fato de que as constantes podem ser inlinadas (e, assim, se tornarem parte da interface binária da sua classe). Outra a observar é que a adição de argumentos padrão pode transformar um construtor explícito de vários argudos em um construtor implícito de um argumento:

class Vehicle {
public:
  Vehicle(int wheels, std::string name = "Mini");
};

Vehicle x = 5;  // this compiles just fine... did you really want it to?

Essa discussão se aplica aos construtores, mas também a métodos e funções.

Usando parâmetros padrão?

O bom é que você não precisará sobrecarregar construtores/métodos/funções para cada caso:

// Header
void doSomething(int i = 25) ;

// Source
void doSomething(int i)
{
   // Do something with i
}

O ruim é que você deve declarar seu padrão no cabeçalho, para ter uma dependência oculta: como quando você altera o código de uma função inlinada, se alterar o valor padrão no seu cabeçalho, precisará recompilar todas as fontes de todas as fontes Usando este cabeçalho para ter certeza de que eles usarão o novo padrão.

Caso contrário, as fontes ainda usarão o valor padrão antigo.

Usando construtores/métodos/funções sobrecarregados?

O bom é que, se suas funções não estiverem inlinadas, você controla o valor padrão na fonte escolhendo como uma função se comportará. Por exemplo:

// Header
void doSomething() ;
void doSomething(int i) ;

// Source

void doSomething()
{
   doSomething(25) ;
}

void doSomething(int i)
{
   // Do something with i
}

O problema é que você precisa manter vários construtores/métodos/funções e seus encaminhamentos.

Na minha experiência, os parâmetros padrão parecem legais na época e fazem meu fator de preguiça feliz, mas depois no caminho estou usando a aula e fico surpreso quando o padrão entra em ação. Então, eu realmente não acho que seja uma boa ideia ; Melhor ter um nome de classe :: classe () e depois um nome de classe :: init (arglist). Apenas para essa borda de manutenção.

Sam's A resposta fornece a razão pela qual os argumentos padrão são preferíveis aos construtores, em vez de sobrecarregar. Eu só quero adicionar que C ++-0x permitirá delegação De um construtor para outro, removendo assim a necessidade de padrões.

Qualquer abordagem funciona. Mas se você tiver uma longa lista de parâmetros opcionais, faça um construtor padrão e, em seguida, sua função definir retorne uma referência a isso. Em seguida, encorrente os coletores.

class Thingy2
{
public:
    enum Color{red,gree,blue};
    Thingy2();

    Thingy2 & color(Color);
    Color color()const;

    Thingy2 & length(double);
    double length()const;
    Thingy2 & width(double);
    double width()const;
    Thingy2 & height(double);
    double height()const;

    Thingy2 & rotationX(double);
    double rotationX()const;
    Thingy2 & rotatationY(double);
    double rotatationY()const;
    Thingy2 & rotationZ(double);
    double rotationZ()const;
}

main()
{
    // gets default rotations
    Thingy2 * foo=new Thingy2().color(ret)
        .length(1).width(4).height(9)
    // gets default color and sizes
    Thingy2 * bar=new Thingy2()
        .rotationX(0.0).rotationY(PI),rotationZ(0.5*PI);
    // everything specified.
    Thingy2 * thing=new Thingy2().color(ret)
        .length(1).width(4).height(9)
        .rotationX(0.0).rotationY(PI),rotationZ(0.5*PI);
}

Agora, ao construir os objetos, você pode escolher quais propriedades substituir e quais você definirá explicitamente nomeado. Muito mais legível :)

Além disso, você não precisa mais se lembrar da ordem dos argumentos ao construtor.

Mais uma coisa a considerar é se a classe poderia ou não ser usada em uma matriz:

foo bar[400];

Nesse cenário, não há vantagem em usar o parâmetro padrão.

Isso certamente não funcionaria:

foo bar("david", 34)[400]; // NOPE

Se a criação de construtores com argumentos é ruim (como muitos argumentam), em seguida, fazer-lhes com argumentos padrão é ainda pior.Eu recentemente comecei a vir ao redor, o parecer que ctor argumentos são ruins, porque a sua lógica deve ctor ser o mínimo possível,.Como você lida com a manipulação de erro no construtor, alguém deve passar em um argumento que não faz qualquer sentido?Você pode lançar uma exceção, que é uma má notícia, a menos que todas as suas chamadas são preparados para encapsular qualquer "novo" chama dentro de blocos try, ou a definição de alguns "é inicializada" variável de membro, que é uma espécie de dirty hack.

Portanto, a única maneira de se certificar de que os argumentos passados para a fase de inicialização de seu objeto é configurar outro método initialize (), onde você pode verificar o código de retorno.

O uso de argumentos padrão é ruim por dois motivos;primeiro de tudo, se você deseja adicionar um outro argumento para o construtor, então você está preso colocando-o no início e mudando toda a API.Além disso, a maioria dos programadores estão acostumados a descobrir uma API pela forma que ele é usado na prática-isto é especialmente verdade para não-API pública é usada dentro de uma organização onde a documentação formal pode não existir.Quando outros programadores ver que a maioria das chamadas não conter argumentos, eles vão fazer o mesmo, mantendo-se alegremente inconscientes de que o padrão de comportamento de seus argumentos padrão impor.

Além disso, vale notar que o o google C++ guia de estilo desviando-se ambos os argumentos do construtor (a menos que seja absolutamente necessário), e padrão argumentos para funções ou métodos.

Gostaria de ir com os parâmetros padrão, por esta razão:O exemplo assume que o construtor parâmetros correspondem diretamente às variáveis de membro.Mas o que se não for o caso, e você tem que processar os parâmetros antes que o objeto é inicializado.Ter um construtor seria o melhor caminho a percorrer.

Uma coisa que me incomoda com os parâmetros padrão é que você não pode especificar os últimos parâmetros, mas usa os valores padrão para os primeiros. Por exemplo, no seu código, você não pode criar um Foo sem nome, mas uma determinada idade (no entanto, se bem me lembro, isso será possível no C ++ 0x, com a sintaxe de construção unificada). Às vezes, isso faz sentido, mas também pode ser realmente estranho.

Na minha opinião, não há regra geral. Pessoal, eu costumo usar vários construtores sobrecarregados (ou métodos), exceto se apenas o último argumento precisar de um valor padrão.

Principalmente escolha pessoal. No entanto, a sobrecarga pode fazer qualquer coisa que o parâmetro padrão possa fazer, mas não vice-versa.

Exemplo:

Você pode usar sobrecarga para gravar um (int x, foo & a) e um (int x), mas não pode usar o parâmetro padrão para gravar um (int x, foo & = null).

A regra geral é usar o que faz sentido e tornar o código mais legível.

Matéria de estilo, mas como Matt disse, definitivamente considere a marcação de construtores com argumentos padrão que permitiriam a conversão implícita como 'explícita' para evitar conversão automática não intencional. Não é um requisito (e pode não ser preferível se você estiver fazendo uma classe de wrapper à qual deseja converter implicitamente), mas pode evitar erros.

Pessoalmente, gosto de padrões quando apropriado, porque não gosto de código repetido. Ymmv.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top