Sobrecarregar operador << - C ++
-
03-07-2019 - |
Pergunta
Fundo
Eu tenho uma classe de contêiner que usa vector
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
- é a sobrecarga de operador escritas corretamente?
- É uma boa prática para operadores de sobrecarga em situações como esta?
- 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.
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.