Substituir e sobrecarga em C++
-
06-07-2019 - |
Pergunta
Sim, eu entendo a diferença entre eles.O que eu quero saber é:por que SUBSTITUIR um método?O que é o bom em fazê-lo?Em caso de sobrecarga:a única vantagem é você não pensar em nomes diferentes para as funções?
Solução
Sobrecarga geralmente significa que você tem duas ou mais funções no mesmo âmbito que tem o mesmo nome. A função que melhor corresponda aos argumentos quando uma chamada é feita vitórias e é chamado. Importante notar, em oposição ao chamar uma função virtual, é que a função que é chamado é selecionado em tempo de compilação. Tudo depende do tipo estático do argumento. Se você tem uma sobrecarga para B
e um para D
, eo argumento é uma referência a B
, mas realmente aponta para um objeto D
, em seguida, a sobrecarga para B
é escolhido em C ++. Isso é chamado expedição estática em oposição à expedição dinâmica . Você sobrecarregar se você quiser fazer o mesmo que outra função com o mesmo nome, mas você quer fazer isso para outro tipo de argumento. Exemplo:
void print(Foo const& f) {
// print a foo
}
void print(Bar const& bar) {
// print a bar
}
ambos imprimir seu argumento, então eles estão sobrecarregados. Mas as primeiras impressões um foo, e as segundas impressões de um bar. Se você tem duas funções que fazem coisas diferentes, é considerado mau estilo quando eles têm o mesmo nome, porque isso pode levar a confusão sobre o que vai acontecer, na verdade, ao chamar as funções. Outro caso de uso para a sobrecarga é quando você tem parâmetros adicionais para as funções, mas o controle apenas para a frente a outras funções:
void print(Foo & f, PrintAttributes b) {
/* ... */
}
void print(Foo & f, std::string const& header, bool printBold) {
print(f, PrintAttributes(header, printBold));
}
Isso pode ser conveniente para o chamador, se as opções que as sobrecargas tomar são usados ??frequentemente.
Substituir é algo completamente diferente. Ele não compete com a sobrecarga. Isso significa que se você tem uma função virtual em uma classe base, você pode escrever uma função com a mesma assinatura na classe derivada. A função na classe derivada substituições a função da base. Amostra:
struct base {
virtual void print() { cout << "base!"; }
}
struct derived: base {
virtual void print() { cout << "derived!"; }
}
Agora, se você tem um objeto e chamar a função membro print
, a função de impressão do derivado é sempre chamado, porque ele substitui o da base. Se a função print
não era virtual, em seguida, a função na derivada não substituir a função de base, mas seria apenas hide -lo. Substituindo pode ser útil se você tem uma função que aceita uma classe base, e cada um que é derivado dele:
void doit(base &b) {
// and sometimes, we want to print it
b.print();
}
Agora, mesmo em tempo de compilação o compilador só sabe que b é de pelo menos base, impressão da classe derivada será chamado. Esse é o ponto de funções virtuais. Sem eles, a função de impressão da base seria chamado, e aquele na classe derivada não iria substituí-lo.
Outras dicas
Isto irá adicionar mais alguma clareza aos pensamentos.
Você sobre load funções para três razões:
-
Para fornecer duas (ou mais) funções que executam, coisas intimamente relacionadas semelhantes, diferenciados por tipos e / ou número de argumentos que aceita. exemplo artificial:
void Log(std::string msg); // logs a message to standard out void Log(std::string msg, std::ofstream); // logs a message to a file
-
Para fornecer duas (ou mais) formas de executar a mesma ação. exemplo artificial:
void Plot(Point pt); // plots a point at (pt.x, pt.y) void Plot(int x, int y); // plots a point at (x, y)
-
Para proporcionar a capacidade de efectuar uma acção equivalente dado duas (ou mais) diferentes tipos de entrada. exemplo artificial:
wchar_t ToUnicode(char c); std::wstring ToUnicode(std::string s);
Em alguns casos vale a pena, argumentando que a função de um nome diferente é uma escolha melhor do que uma função sobrecarregada. No caso de construtores, a sobrecarga é a única opção.
Sobre montando a função é completamente diferente, e serve a um propósito totalmente diferente. Função primordial é como polimorfismo funciona em C ++. Você substituir uma função para alterar o comportamento dessa função em uma classe derivada. Desta forma, uma classe base fornece interface, ea classe derivada fornece implementação.
Substituir é útil quando você herda de uma classe base e pretendam ampliar ou modificar a sua funcionalidade. Mesmo quando o objeto é lançado como a classe base, ele chama a sua função substituída, não a base.
A sobrecarga não é necessário, mas certamente torna a vida mais fácil ou mais legíveis às vezes. Sem dúvida ele pode torná-lo pior, mas isso é quando ele não deve ser usado. Por exemplo, você pode ter duas funções que executam a mesma operação, mas agir em diferentes tipos de coisas. Por exemplo Divide(float, float)
deve ser diferente do Divide(int, int)
, mas eles são basicamente a mesma operação. Você não gostaria de lembrar um nome do método, "Divide", que tem que se lembrar "DivideFloat", "DivideInt", "DivideIntByFloat", e assim por diante?
As pessoas já definidos tanto a sobrecarga e substituindo, então não vou discorrer.
ASAFE perguntou:
a única vantagem de sobrecarga] é você não pensar em vários nomes de funções?
1.Você não tem que pensar em vários nomes
E isso já é uma poderosa vantagem, não é?
Vamos comparar com o conhecido funções da API C, e a sua ficção C++ variantes:
/* C */
double fabs(double d) ;
int abs(int i) ;
// C++ fictional variants
long double abs(long double d) ;
double abs(double d) ;
float abs(float f) ;
long abs(long i) ;
int abs(int i) ;
Isso significa duas coisas:Um, você deve informar ao compilador o tipo de dados que ele irá alimentar para a função de escolha de função direita.Dois, se você deseja estender-lo, você precisará encontrar nomes de fantasia, e o usuário de suas funções terá que lembre o direito de nomes de fantasia.
E tudo o que ele/Ela queria era ter o valor absoluto de alguns variável numérica...
Uma ação significa uma e apenas uma função de nome.
Observe que você não está limitado para alterar o tipo de um parâmetro.Tudo pode mudar, desde que não faz sentido.
2.Para os operadores, é obrigatório
Vamos ver de operadores:
// C++
Integer operator + (const Integer & lhs, const Integer & rhs) ;
Real operator + (const Real & lhs, const Real & rhs) ;
Matrix operator + (const Matrix & lhs, const Matrix & rhs) ;
Complex operator + (const Complex & lhs, const Complex & rhs) ;
void doSomething()
{
Integer i0 = 5, i1 = 10 ;
Integer i2 = i0 + i1 ; // i2 == 15
Real r0 = 5.5, r1 = 10.3 ;
Real r2 = r0 + r1 ; // r2 = 15.8
Matrix m0(1, 2, 3, 4), m1(10, 20, 30, 40) ;
Matrix m2 = m0 + m1 ; // m2 == (11, 22, 33, 44)
Complex c0(1, 5), c1(10, 50) ;
Complex c2 = c0 + c1 ; // c2 == (11, 55)
}
No exemplo acima, você quer para evitar o uso de qualquer coisa que não seja o operador+.
Note que C tem implícito sobrecarregar do operador para tipos internos (incluindo C99 tipo complexo):
/* C */
void doSomething(void)
{
char c = 32 ;
short s = 54 ;
c + s ; /* == C++ operator + (char, short) */
c + c ; /* == C++ operator + (char, char) */
}
Assim, mesmo em não-objeto idiomas, esta sobrecarga coisa é utilizado.
3.Para objetos, é obrigatória
Vamos ver o uso de um objeto de métodos básicos:Seus construtores:
class MyString
{
public :
MyString(char character) ;
MyString(int number) ;
MyString(const char * c_style_string) ;
MyString(const MyString * mySring) ;
// etc.
} ;
Alguns poderiam considerar isto como função de sobrecarga, mas, na verdade, é mais semelhante ao operador de sobrecarga:
void doSomething()
{
MyString a('h') ; // a == "h" ;
MyString b(25) ; // b == "25" ;
MyString c("Hello World") ; // c == "Hello World" ;
MyString d(c) ; // d == "Hello World" ;
}
Conclusão:A sobrecarga é legal
No C, quando você dar o nome da função, os parâmetros são implicitely parte da assinatura na chamada.Se você tem "duplo fab(double d)" e, em seguida, enquanto a assinatura da fab para o compilador é o undecorated "fab", isso significa que você deve saber que leva apenas duplica.
Em C++, o nome da função não significa que sua assinatura é forçado.A sua assinatura a chamada de seu nome e seus parâmetros.Assim, se você escrever abs(-24), o compilador irá saber que a sobrecarga do abs tem de chamar, e você, ao escrevê-lo, encontrá-lo mais natural:Você deseja que o valor absoluto de -24.
De qualquer maneira, quem codificadas um pouco em qualquer idioma, com os operadores já usa a sobrecarga, seja C ou Basic numérica operadores, Java concatenação de seqüência de caracteres, C# delegados, etc..Por quê? porque é mais natural.
E os exemplos acima são apenas a ponta do iceberg:Quando utilizar modelos, sobrecarregando-se tornar muito útil, mas isso é outra história.
O exemplo clássico é animal classe com speak () método. Substitui o cão subclasse falar () a "casca", enquanto substituições do gato subclasse falar () para "miau".
Um uso de sobrecarga é para uso em templates. Em modelos, você escrever um código que pode ser usado em diferentes tipos de dados, e chamá-lo com diferentes tipos. Se funções que recebem diferentes argumentos tiveram de ser nomeados de forma diferente, o código para diferentes tipos de dados que, em geral, tem que ser diferente, e modelos apenas não iria funcionar.
Enquanto você não pode escrever modelos, no entanto, você está quase certo com alguns deles. Streams são modelos, e por isso são vetores. Sem sobrecarregar, e, portanto, sem modelos, você precisa chamar Unicode córregos algo diferente a partir de fluxos ASCII, e você teria que usar matrizes e ponteiros em vez de vetores.