Pergunta

Fundo

Eu tenho uma classe de contêiner que usa vector internamente. Eu tenho desde um método AddChar (std :: string) para esta classe de invólucro que faz um push_back () para o vetor interno. No meu código, eu tenho que adicionar vários itens para o recipiente de algum tempo. Por que eu tenho que usar

container.AddChar("First");
container.AddChar("Second");

Isso faz com que o código maior. Então, para tornar mais fácil mais, eu pretendo operador de sobrecarga <<. Para que eu possa escrever

container << "First" << "Second"

e dois itens vai ser adicionados ao subjacente vetor.

Aqui está o código que usei para esse

class ExtendedVector
{
private:
    vector<string> container;

public:
    friend ExtendedVector& operator<<(ExtendedVector& cont,const std::string str){
        cont.AddChar(str);
        return cont;
    }

    void AddChar(const std::string str)
    {
        container.push_back(str);
    }

    string ToString()
    {
        string output;
        vector<string>::iterator it = container.begin();
        while(it != container.end())
        {
            output += *it;
            ++it;
        }
        return output;
    }
};

Trabalhando como Espera-se.

Perguntas

  1. é a sobrecarga de operador escritas corretamente?
  2. É uma boa prática para operadores de sobrecarga em situações como esta?
  3. Será que vai haver quaisquer problemas de desempenho ou quaisquer outros problemas com este código?

Qualquer pensamento?

Editar

Depois de ouvir as excelentes comentários, eu não decidiu sobrecarga << uma vez que não faz sentido aqui. I removido o código de sobrecarga de operador e aqui está o código final.

class ExtendedVector
{
private:
    vector<string> container;

public:

    ExtendedVector& AddChar(const std::string str)
    {
        container.push_back(str);
        return *this;
    }

         .. other methods
}

Isso me permite adicionar

container.AddChar("First").AddChar("Second")

Em C #, eu posso fazer isso mais facilmente usando a palavra-chave params. Código será como

void AddChar(params string[] str)
{
    foreach(string s in str)
       // add to the underlying collection
}

Eu sei que em C ++, podemos usar ... para especificar langth variável de parâmetros. Mas AFAIK, não é tipo seguro. Então, é uma prática recomendada para fazê-lo? Para que eu possa escrever

container.AddChar("First","Second")

Obrigado pelas respostas.

Foi útil?

Solução

é a sobrecarga de operador escritas corretamente?

É, mas pode fazer melhor. Como alguém referiu, a sua função pode ser definida inteiramente fora de funções existentes, públicas. Por que não fazê-lo usar apenas aqueles? Agora, ele é um amigo, o que significa que pertence aos detalhes de implementação. O mesmo é verdade se você colocar operador << como um membro em sua classe. No entanto, fazer o seu operador de << um não-membro , não-amigo função.

class ExtendedVector {
    ...
};

// note, now it is *entirely decoupled* from any private members!
ExtendedVector& operator<<(ExtendedVector& cont, const std::string& str){
    cont.AddChar(str);
    return cont;
}

Se você mudar de classe, você não vai ter certeza de que se o operador << continuará a funcionar. Mas se o seu operador << inteiramente depende apenas de funções públicas, então você pode ter certeza que ele vai trabalhar após as alterações foram feitas para os detalhes de implementação de apenas a sua classe. Yay!

É uma boa prática para operadores de sobrecarga em situações como esta?

Como outro cara disse novamente, isso é discutível. Em muitas situações, sobrecarga de operadores vão olhar "puro" à primeira vista, mas será parecido com o inferno no próximo ano, porque você não tem nenhum indício mais que você tinha em mente quando dando alguns símbolos amor especial. No caso do operador <<, penso que este é um uso OK. Seu uso como um operador de inserção para fluxos é bem conhecida. E eu sei de aplicações Qt e do KDE que o utilizam extensivamente em casos como

QStringList items; 
items << "item1" << "item2";

Um caso semelhante é boost.format que também reutiliza operator% para passar argumentos para espaços reservados em sua string:

format("hello %1%, i'm %2% y'old") % "benny" % 21

É claro que também discutível para usá-lo lá. Mas seu uso para especificar o formato printf são bem conhecidos e por isso seu uso é OK lá também, imho. Mas, como sempre, o estilo também é subjetiva para levá-lo com um grão de sal:)

Como eu posso aceitar argumentos de comprimento variável de uma maneira typesafe?

Bem, não é a maneira de aceitar um vector se você está procurando argumentos homogêneas:

void AddChars(std::vector<std::string> const& v) {
    std::vector<std::string>::const_iterator cit =
        v.begin();
    for(;cit != v.begin(); ++cit) {
        AddChar(*cit);
    }
}

Não é realmente confortável para passá-lo embora. Você tem que construir o seu vector manualmente e, em seguida, passar ... Eu vejo que você já tem o sentimento certo sobre as funções de estilo vararg. Não se deve usá-los para este tipo de código e apenas quando a interface com código C ou depurar funções se em tudo. Outra maneira de lidar com este caso é a aplicação de pré-processador de programação. Este é um tópico avançado e é bastante hacky. A ideia é gerar automaticamente sobrecargas até certo limite superior mais ou menos assim:

#define GEN_OVERLOAD(X) \
void AddChars(GEN_ARGS(X, std::string arg)) { \
    /* now access arg0 ... arg(X-1) */ \
    /* AddChar(arg0); ... AddChar(arg(N-1)); */ \
    GEN_PRINT_ARG1(X, AddChar, arg) \
}

/* call macro with 0, 1, ..., 9 as argument
GEN_PRINT(10, GEN_OVERLOAD)

Isso é pseudo-código. Você pode ter um olhar para a biblioteca de impulso pré-processador aqui .

Próxima versão C ++ vai oferecer muito melhores possibilidades. listas inicializador pode ser usado:

void AddChars(initializer_list<std::string> ilist) {
    // range based for loop
    for(std::string const& s : ilist) {
        AddChar(s);
    }
}

...
AddChars({"hello", "you", "this is fun"});

Também é possível na próxima C ++ para apoiar muitos argumentos arbitrários (de tipo misto) usando variádica modelos . GCC4.4 terá suporte para eles. GCC 4.3 já parcialmente apoia-los.

Outras dicas

1) Sim, com exceção desde AddChar é pública não há nenhuma razão que ele precisa ser um friend.

2) Este é discutível. << é uma espécie de na posição de ser o operador cuja sobrecarga para as coisas "estranho" é, pelo menos, a contragosto aceita.

3) Nada óbvio. Como sempre, profiling é seu amigo. Você pode querer considerar passando os parâmetros de cadeia para AddChar e operator<< por referência const (const std::string&) para evitar cópias desnecessárias.

É uma boa prática para a sobrecarga operadores em situações como esta?

Eu não penso assim. É confuso como o inferno para alguém que não sabe o que você já sobrecarregado o operador. Se ater apenas a nomes de métodos descritivos e esquecer os caracteres extras que você está digitando, é apenas não vale a pena. O seu mantenedor (ou você mesmo em 6 meses) vai agradecer.

Eu prefiro não sobrecarregar-lo dessa forma, pessoalmente, porque vetores normalmente não têm um operador de deslocamento esquerdo sobrecarregado - isso não é realmente a sua linguagem; -)

Eu provavelmente retornar uma referência de AddChar vez assim:

ExtendedVector& AddChar(const std::string& str) {
    container.push_back(str);
    return *this;
}

para que possa depois fazer

container.AddChar("First").AddChar("Second");

que não é realmente muito maior do que os operadores bitshift.

(ver também o comentário de Logan sobre a passagem cordas no por referência e não por valor).

O operador sobrecarga neste caso não é uma boa prática, uma vez que torna o código menos legível. O std::vector norma não tê-lo, quer para empurrar elementos, por boas razões.

Se você está preocupado com o código chamador ser demasiado longo, você poderia considerar isso em vez do operador sobrecarregado:

container.AddChar("First").AddChar("Second");

Isso será possível se você tiver AddChar() retorno *this.

É engraçado que você tem esta função toString(). Em que caso, um operator<< a saída para um fluxo seria a coisa padrão para usar em vez! Então, se você quiser operadores de uso fazer a função toString() um operator<<.

O operador não foi correctamente sobrecarregado aqui. Não há nenhuma razão para fazer o operador um amigo, uma vez que pode ser um membro da classe. Amigo é para funções que não são membros reais da classe (como quando a sobrecarga << para ostream de modo que o objeto pode ser a saída para cout ou ofstreams).

O que você realmente deseja que o operador a ser:

ExtendedVector& operator<<(const std::string str){
    AddChar(str);
    return *this;
}

É geralmente considerado má prática aos operadores de sobrecarga de uma forma que tem-los a fazer algo que eles fazem normalmente. << é normalmente mudança pouco, para sobrecarregar-lo desta maneira pode ser confuso. Obviamente sobrecargas STL << para "inserção de fluxo", e assim, juntamente com que ele pode faz sentido para sobrecarregá-lo para seu uso de forma semelhante. Mas isso não parece ser o que você está fazendo, então você provavelmente vai querer evitá-lo.

Não há problemas de desempenho desde a sobrecarga de operador é o mesmo que uma chamada de função regular, apenas a chamada está invisível pois é feito automaticamente pelo compilador.

Isso vai tornar as coisas um pouco confuso, eu usaria a mesma sintaxe que std :: cin em uma variável:

std::cin >> someint;

"First" >> container;

Desta forma é pelo menos um operador de inserção. Para mim, quando tudo tem um << operador sobrecarregado Eu esperava que fosse produzir alguma coisa. Assim como std :: cout.

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