In C++, we have the flexibility to change the access level of an inherited member in a derived class. This is achieved using a using declaration, which identifies the base class member and specifies the new access specifier in the derived class.
Consider the following example with a base class Base
:
#include <iostream>
class Base {
private:
int m_value;
public:
Base(int value) : m_value(value) {}
protected:
void printValue() const { std::cout << m_value; }
};
In this case, Base::printValue()
is declared as protected, making it accessible only to Base
and its derived classes. Now, let's define a derived class Derived
that changes the access specifier of printValue()
to public:
class Derived : public Base {
public:
Derived(int value) : Base(value) {}
// Changing the access specifier of Base::printValue to public
using Base::printValue;
};
Now, the following code will work:
int main() {
Derived derived{7};
derived.printValue(); // Prints 7
return 0;
}
This demonstrates that by using a using declaration, we can alter the access specifier of an inherited member.
Hiding Functionality
In C++, it's not possible to remove or restrict functionality from a base class directly, but it's feasible to hide functionality in a derived class by changing the access specifier. For example, making a public member private in the derived class:
#include <iostream>
class Base {
public:
int m_value;
};
class Derived : public Base {
private:
using Base::m_value;
public:
Derived(int value) : Base(value) {}
};
In this case, attempting to access, m_value
directly in the derived class will result in an error, enhancing encapsulation.
However, it's essential to note that while m_value
is private in the derived class, it remains pubic in the base class. Thus, the encapsulation in the derived class can be bypassed by casting to Base&
and accessing the member directly.
Deleting Functions in the Derived Class
Functions in the derived class can be marked as deleted, preventing them from being called through a derived object:
#include <iostream>
class Base
{
private:
int m_value {};
public:
Base(int value)
: m_value { value }
{
}
int getValue() const { return m_value; }
};
class Derived : public Base
{
public:
Derived(int value)
: Base { value }
{
}
int getValue() const = delete; // mark this function as inaccessible
};
int main()
{
Derived derived { 7 };
// The following won't work because getValue() has been deleted!
std::cout << derived.getValue();
return 0;
}
In this example, attempting to call derived.getValue(
) will result in a compilation error. However, the Base version of getValue()
remains accessible, allowing it to be called using Base::getValue()
or by upcasting Derived
to a Base()
reference.