Redefining vs. Overriding in C++
Asked Answered
J

2

21

I am confused about the differences between redefining and overriding functions in derived classes.

I know that - In C++, redefined functions are statically bound and overridden functions are dynamically bound and that a a virtual function is overridden, and a non-virtual function is redefined.

When a derived class "redefines" a method in a base class its considered redefining. But when a derived class is virtual it no longer redefines but rather overrides. So i understand the logistics of the rules but i don't understand the bottom line.

In the below example the function SetScore is redefined. However if I make the setScore function in the base class virtual (by adding the word virtual to it) the setScore in the derived class will be overridden. I don't understand the bottom line - what is the difference. in setScore?

The base class:

class GradedActivity
{
protected:
   char letter;            // To hold the letter grade
   double score;           // To hold the numeric score
   void determineGrade();  // Determines the letter grade
public:
   // Default constructor
   GradedActivity()
  { letter = ' '; score = 0.0; }

 // Mutator function
   void setScore(double s) 
      { score = s;
        determineGrade();}

   // Accessor functions
   double getScore() const
      { return score; }

       char getLetterGrade() const
      { return letter; }
};

The derived class:

class CurvedActivity : public GradedActivity
{
protected:
   double rawScore;     // Unadjusted score
   double percentage;   // Curve percentage
public:
   // Default constructor
   CurvedActivity() : GradedActivity()
      { rawScore = 0.0; percentage = 0.0; }

   // Mutator functions
   void setScore(double s) 
      { rawScore = s;
        GradedActivity::setScore(rawScore * percentage); }

   void setPercentage(double c)
      { percentage = c; }

   // Accessor funtions
   double getPercentage() const
      { return percentage; }

   double getRawScore() const
      { return rawScore; }
};

This is main:

   // Define a CurvedActivity object.
   CurvedActivity exam;

   ... 

   // Send the values to the exam object.
   exam.setPercentage(percentage);
   exam.setScore(numericScore);
Juniejunieta answered 4/11, 2014 at 20:3 Comment(1)
Overriding facilitates class polymorphism. Overloading facilitates functional polymorphism. Redefining does neither and is an error.Laminitis
A
33

Here are some basic differences:

An overloaded function is a function that shares its name with one or more other functions, but which has a different parameter list. The compiler chooses which function is desired based upon the arguments used.

An overridden function is a method in a descendant class that has a different definition than a virtual function in an ancestor class. The compiler chooses which function is desired based upon the type of the object being used to call the function.

A redefined function is a method in a descendant class that has a different definition than a non-virtual function in an ancestor class. Don't do this. Since the method is not virtual, the compiler chooses which function to call based upon the static type of the object reference rather than the actual type of the object.

  • Static type checking means that type checking occurs at compile time. No type information is used at runtime in that case.

  • Dynamic type checking occurs when type information is used at runtime. C++ uses a mechanism called RTTI (runtime type information) to implement this. The most common example where RTTI is used is the dynamic_cast operator which allows downcasting of polymorphic types:

Asturias answered 4/11, 2014 at 20:7 Comment(5)
Thanks, this may be stupid but what does this mean: static type of the object reference.Juniejunieta
Functions only overload if in the same scope. Might want to add that.Atwater
"The compiler chooses which function is desired based upon the type of the object being used to call the function." That's not correct, the compiler doesn't decide anything. The decision is done at runtime via vtable lookup.Blankly
@Juniejunieta If class B derives from class A and we have A MyA; and B MyB; and do MyA=MyB; Then static type of MyA is being an A, while right now (dynamically) MyA is storing a B (its dynamic type is B). If we latter have class C deriving from B and have C MyC; and do MyA = MyC, once more the dynamic type of MyA changed to being a C. The static type of MyA continues being an A.Landlordism
"A redefined function is a method in a descendant class that has a different definition than a non-virtual function in an ancestor class. Don't do this." It prevents vtable lookup and the language allows that if the gain is worth it. Of course the implementation should watch out for the mentioned pitfall.Haggard
F
4

Giving Examples to the Lawrence Aiello's answer

Function Overloading (must be in the SAME scope):

int add(int a, int b) {
    return a + b;
}

double add(double a, double b) {
    return a + b;
}

Function Redefinition:

class Base {
public:
    // notice that the function is NOT virtual
    void greetings() const {
        cout << "Hi, I'm from Base class" << endl;
    }
};

class Derived : public Base {
public:
    // redefinition of the function
    void greetings() const {
        cout << "Hi, I'm from Derived class" << endl;
    }
};

Base b;
b.greetings(); // Output: Hi, I'm from Base class
// static binding

Derived d;
d.greetings();  // Output: Hi, I'm from Derived class
// static binding

Base *b_ptr = new Derived();
b_ptr->greetings(); // Output: Hi, I'm from Base class <== PROBLEM
                    // Expected: Hi, I'm from Derived class
// static binding
// Compiler all knows that b_ptr is a pointer to Base object, so it binds the
// method Base::greetings at compile time.

Function Overriding:

class Base {
public:
    // notice that the function is virtual
    virtual void greetings() const {
        cout << "Hi, I'm from Base class" << endl;
    }
};

class Derived : public Base {
public:
    // overriding the function
    void greetings() const {
        cout << "Hi, I'm from Derived class" << endl;
    }
};

Base b;
b.greetings(); // Output: Hi, I'm from Base class
// static binding

Derived d;
d.greetings();  // Output: Hi, I'm from Derived class
// static binding

Base *b_ptr = new Derived();
b_ptr->greetings();   // Output: Hi, I'm from Derived class (Expected)
// dynamic binding
// The idea of using virtual functions tells the compiler NOT to bind the 
// method at compile-time, but instead defer to run-time.
Fivepenny answered 2/1, 2023 at 7:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.