C++ Class Methods
June 10, 2020 by Jane

Methods:

Declare all accessor functions with "const" keyword.

Sometimes there will be read-only copies of the same method, with and without "const"

E.g.

class Test {
public:   
    void print() const { cout << "const " << endl; }
    void print() { cout << "not const" << endl; }
};

int main() {

    Test t;

    // prints "not const" because t is not declared with "const" modifier

    t.print(); 

   

    const Test c;

    // prints "const" because c was declared with "const" modifier; it can only invoke the const methods

    c.print(); 

}

Note that if we didn't have a const print() method the program above would not compile. Instead it would give an error that says: cannot convert "this" pointer from "const Test" to "Test &"

 

Inherit vs. extend vs. replace base class methods:

Inherit: makes no modifications to the base class's implementation of the method, basically will not mention the method in the derived class

E.g. 

class A {
public:   
    void print() { cout << "print" << endl; }
};

class B : public A {

};

In this example class B inherits the print() method from class A. It doesn't mention the method anywhere in its class definition but any instance of class B will be able to call the  print() method because it has been inherited

 

Extend: makes an implementation of the base class method and calls the base class method implementation with [baseClassName]::method() then adds its own customization

E.g.

class A {
public:   
    void print() { cout << "print" << endl; }
};

class B : public A {

public:

    void print() { A::print(); cout << "B prints" << endl; }

};

In this example class B inherits the print() method but overrides it by defining a method with the same signature. Inside its implementation it calls the inherited method print(), here we're using the scope resolution symbol (::) to indicate we want to call the print() implementation from class A. This is an examples of B extending the implementation of the print() method from A.

 

Note that if we didn't use the scope resolution symbol to indicate we're calling the base class implementation (A::print()), the derived implementation (B::print()) would be called and result in an infinite loop. 

 

Replace: supply a new implementation of the method without any reference to the base class's implmentation

E.g. 

class A {
public:   
    void print() { cout << "print" << endl; }
};

class B : public A {

public:

    void print() { cout << "B prints" << endl; }

};

In this example class B inherits the print() method but overrides it without any reference to the inherited method. The print() method from A will not be called at all when print is invoked on an instance of B. In other words, the print() method has been entirely replaced in class B.

 

Shadowing:

E.g. 

class A {

public:

    int foo()  { cout << "A::foo"; }

    int foo(int i)  { cout << "A::foo with i"; }

};

class B: public A {

public:

    int foo() {  cout << "B::foo"; }

};

int main() {

    B b;

    b.foo(5);

}

What is the expected behaviour here? It will not compile. The compiler will probably complain about there being too many arguments in the function call.

The reason for this is if a derived class writes its own method, then all functions of base class with same name become hidden or shadowed, even if signaures of base class functions are different. The foo in class B hides all of the implementations of foo from class A, even the overloaded ones.

 

How to solve this? Use the scope resolution operator to call class A's implementation:

b.A::foo(5); // will print A::foo with i