Yes, I do understand the difference between them. What I want to know is: why OVERRIDE a method? What is the good in doing it? In case of overload: the only advantage is you haven't to think in different names to functions?
问题:
回答1:
Overloading generally means that you have two or more functions in the same scope having the same name. The function that better matches the arguments when a call is made wins and is called. Important to note, as opposed to calling a virtual function, is that the function that's called is selected at compile time. It all depends on the static type of the argument. If you have an overload for B
and one for D
, and the argument is a reference to B
, but it really points to a D
object, then the overload for B
is chosen in C++. That's called static dispatch as opposed to dynamic dispatch. You overload if you want to do the same as another function having the same name, but you want to do that for another argument type. Example:
void print(Foo const& f) {
// print a foo
}
void print(Bar const& bar) {
// print a bar
}
they both print their argument, so they are overloaded. But the first prints a foo, and the second prints a bar. If you have two functions that do different things, it's considered bad style when they have the same name, because that can lead to confusion about what will happen actually when calling the functions. Another usecase for overloading is when you have additional parameters for functions, but they just forward control to other functions:
void print(Foo & f, PrintAttributes b) {
/* ... */
}
void print(Foo & f, std::string const& header, bool printBold) {
print(f, PrintAttributes(header, printBold));
}
That can be convenient for the caller, if the options that the overloads take are often used.
Overriding is something completely different. It doesn't compete with overloading. It means that if you have a virtual function in a base class, you can write a function with the same signature in the derived class. The function in the derived class overrides the function of the base. Sample:
struct base {
virtual void print() { cout << "base!"; }
}
struct derived: base {
virtual void print() { cout << "derived!"; }
}
Now, if you have an object and call the print
member function, the print function of the derived is always called, because it overrides the one of the base. If the function print
wasn't virtual, then the function in the derived wouldn't override the base function, but would merely hide it. Overriding can be useful if you have a function that accepts a base class, and every one that's derived from it:
void doit(base &b) {
// and sometimes, we want to print it
b.print();
}
Now, even though at compile time the compiler only knows that b is at least base, print of the derived class will be called. That's the point of virtual functions. Without them, the print function of the base would be called, and the one in the derived class wouldn't override it.
回答2:
This will add some more clarity to thoughts.
回答3:
You overload functions for three reasons:
To provide two (or more) functions that perform similar, closely related things, differentiated by the types and/or number of arguments it accepts. Contrived example:
void Log(std::string msg); // logs a message to standard out void Log(std::string msg, std::ofstream); // logs a message to a file
To provide two (or more) ways to perform the same action. Contrived example:
void Plot(Point pt); // plots a point at (pt.x, pt.y) void Plot(int x, int y); // plots a point at (x, y)
To provide the ability to perform an equivalent action given two (or more) different input types. Contrived example:
wchar_t ToUnicode(char c); std::wstring ToUnicode(std::string s);
In some cases it's worth arguing that a function of a different name is a better choice than an overloaded function. In the case of constructors, overloading is the only choice.
Overriding a function is entirely different, and serves an entirely different purpose. Function overriding is how polymorphism works in C++. You override a function to change the behavior of that function in a derived class. In this way, a base class provides interface, and the derived class provides implementation.
回答4:
Override is useful when you inherit from a base class and wish to extend or modify its functionality. Even when the object is cast as the base class, it calls your overridden function, not the base one.
Overloading is not necessary, but it sure makes life easier or more readable sometimes. Arguably it can make it worse, but that's when it should not be used. For example, you can have two functions that perform the same operation, but act on different kinds of things. For example Divide(float, float)
should be different from Divide(int, int)
, but they're basically the same operation. Wouldn't you rather remember one method name, "Divide", than have to remember "DivideFloat", "DivideInt", "DivideIntByFloat", and so on?
回答5:
People already defined both overloading and overriding, so I won't elaborate.
ASAFE asked:
the only advantage [to overloading] is you haven't think in several names to functions?
1. You don't have to think in several names
And this is already a mighty advantage, isn't it?
Let's compare with known C API functions, and their fictional C++ variants:
/* 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) ;
This means two things: One, you must tell the compiler the type of the data it will feed to the function by choosing the right function. Two, if you want to extend it, you'll need to find fancy names, and the user of your functions will have to remember the right fancy names.
And all he/She wanted was to have the absolute value of some numerical variable...
One action means one and only one function name.
Note that you are not limited to change the type of one paramter. Anything can change, as long as it makes sense.
2. For operators, it is mandatory
Let's see of operators:
// 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)
}
In the above example, you do want to avoid using anything else than the + operator.
Note that C has implicit operator overloading for built-in types (including C99 complex type):
/* C */
void doSomething(void)
{
char c = 32 ;
short s = 54 ;
c + s ; /* == C++ operator + (char, short) */
c + c ; /* == C++ operator + (char, char) */
}
So even in non-object languages, this overloading thing is used.
3. For objects, it is mandatory
Let's see the use of an object basic methods: Its constructors:
class MyString
{
public :
MyString(char character) ;
MyString(int number) ;
MyString(const char * c_style_string) ;
MyString(const MyString * mySring) ;
// etc.
} ;
Some could consider this like function overloading, but in fact, it is more similar to operator overloading:
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" ;
}
Conclusion: Overloading is cool
In C, when you give the name of the function, the parameters are implicitely part of the signature at call. If you have "double fabs(double d)", then while the signature of fabs for the compiler is the undecorated "fabs", it means that you must know it takes only doubles.
In C++, the name of the function does not mean its signature is forced. Its signature at call is its name and its parameters. Thus, if you write abs(-24), the compiler will know what overloading of abs it must call, and you, when writing it, find it more natural: You want the absolute value of -24.
Anyway, anyone who coded somewhat in any language with operators already uses overloading, be it C or Basic numerical operators, Java string concatenation, C# delegates, etc.. Why? because it's more natural.
And the examples shown above are just the tip of the iceberg: When using templates, overloading become very very useful, but this is another story.
回答6:
The textbook example is class Animal with method speak(). The Dog subclass overrides speak() to "bark" while the Cat subclass overrides speak() to "meow".
回答7:
One use of overloading is for use in templates. In templates, you write code that can be used on different data types, and call it with different types. If functions that take different arguments had to be named differently, the code for different data types would in general have to be different, and templates just wouldn't work.
While you may not be writing templates yet, you're almost certainly using some of them. Streams are templates, and so are vectors. Without overloading, and therefore without templates, you'd need to call Unicode streams something different from ASCII streams, and you'd have to use arrays and pointers instead of vectors.