Modify Your Base Class Interface in Derived Classes

base class provides an interface that is inherited by derived classes. However, in some cases you need to extend, or even alter, the base class’s interface in a derived class. For example, if you’re overloading a member function of the base class in a derived class or readjusting a member’s access.


How do you overload a member function across a class hierarchy? How do you override the access type of a base class’s member in a derived class?


Use a using-declaration to control a member’s access type and to overload member functions across a class hierarchy.

Demonstrating the Problem
Suppose you’re implementing a file system that uses a class hierarchy like this one:

class File{public: int Open(const string & path); int Read (char * buff, size_t bytes); int Write (char *buff, size_t bytes); //..};class DiskFile: public File{public: int Defragment();//..};class NetworkFile : public DiskFile{//.. };

Class NetworkFile implements a special type of file that can be stored and accessed remotely. At this stage, the designer faces two problems. First, unlike a local disk file, network files require a more rigorous authorization check to ensure that only authorized users may access them. Secondly, because the file may reside on a remote machine, it’s necessary to overload the Open() member function to support additional types of path names, or, wstring names. A naive designer might include an additional overloaded version of this function like this:

class NetworkFile : public DiskFile{public: int Open(const wstring & path);//seemingly overloading //..};

This code compiles okay. However, it doesn’t exactly overload File::Open?instead, it hides it:

string path="\usr\image.bmp";NetworkFile nf;nf.Open(path); //compilation error

Here’s the problem: when a compiler employs the overload resolution algorithm, it stops searching at the first scope in which a viable candidate is found. In this example, it finds NetworkFile::Open(const wstring & ) and the search terminates. However, the argument passed to the function is of type string &. Because of the type mismatch the compilation fails.Name Injection
To fix this problem, you have to explicitly inject the name File::Open into the scope of NetworkFile::Open(). This way, any additional function called Open() that you declare in NetworkFile won’t hide File::Open. To do so, use a using-declaration like this:

class NetworkFile : public DiskFile{public: using File::Open; //inject a name into current scope int Open(const wstring & path); //..};

When you add a using-declaration of this kind, make sure that it appears in the public section of the class. Now you can use both overloaded versions of Open() in the derived class:

string path="\usr\image.bmp";wstring wpath=L"\usr\image.bmp";NetworkFile nf, nf2;nf.Open(path); //OK, File::Open(const string &)nf2.Open(wpath); //OK, NetworkFile::Open(const wstring &)

You may add as many overloaded versions of Open() as you like to NetworkFile:

class NetworkFile : public DiskFile{public: using File:Open; //inject name into current scope int Open(const wstring & path); int Open (const char * path); int Open (const wchar_t * path); //..};

Likewise, any class that inherits from NetworkFile can use the same technique to add even more overloaded functions:

class UnixNetworkFile : public NetworkFile{public: using NetworkFile:Open; //inject all overloaded versions int Open(int descriptor);//..};UnixNetworkFile nf1, nf2, nf3;nf.Open(path);nf.Open(wpath);nf.Open(2); //opens stderr

Controlling Access Type
A using-declaration also enables you to change the access type of a base class’s member in a derived class. Consider a ReadOnly class that enables users to read a file but not to change it. You may need to change the access type of Write() to private. This can be accomplished by adding a using-declaration to the private section of the class:

class ReadOnly: public File{private: //change the access of File::Write using File::Write;};

Consequently, ReadOnly objects cannot invoke this member function:

ReadOnly ro;ro.Write(mybuff);//error: Write() is not accessible

The Buck Stops Here
Each derived class can override a previous access type. For example, if you have a member function f() declared public in class A, and a derived class D which changes the access type of f() to private:

class A{public: int f();protected: int g();};class D: public A{private using A::f(); //OK, f() is now private};

A class derived from D may override the access type of f() once again:

class E : public D{public using A::f(); //OK, f() is public again};

However, you cannot grant a more permissive access type than the one originally specified in the member’s declaration. For example, the member function g() is declared protected in A. Class F that is derived from A can change g()‘s access to private. A class derived from F may subsequently change g()‘s access back to protected. However, none of them can change g()‘s access type to public. This restriction ensures that the using-declaration facility doesn’t violate the fundamental aspects of the C++ object model.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: