Tuesday, July 26, 2022

C++ API Design Best Practices - EXTENDING VIA INHERITANCE


EXTENDING VIA INHERITANCE

An object-oriented mechanism for extending a class is inheritance.

This can be used to let your users define new classes that build upon and modify the functionality of existing classes in your API.

Adding Functionality

  • This is simply extending an existing class where only new methods are added to the base class.
  • An important point to reiterate here is that this can only be done safely if the base class was designed tobe inherited from. The primary indicator for this is whether the class has a virtual destructor.   
  • This is not an issue because when subclass of the new methods are stateless, that is, they do not allocate any memory that must be freed by the base class destructor.
  • However, this does highlight the issue that if you expect your users to inherit from any of your classes, you should declare the destructor for those classes to be virtual. 
  • Declaring a virtual destructor for a class is a signal to your users that you have designed it to be inherited from.

Modifying Functionality

  • C++ allows you to define functions in a derived class that override existing functions in a base class if they have been marked as virtual in the base class.

Prohibiting Subclassing

  • When you want to prohibit users from inheriting derived classes from the classes you provide to them. In Java, you can declare a class to be final to prevent others from inheriting from it, but C++ does not have a similar concept.
  • If you declare a class with a non-virtual destructor, this should be a signal to a good programmer that they should think twice about inheriting from the class. However, if you want a physical mechanism to prevent users from subclassing one of your classes, the easiest way to do this is to make all of its constructors private. Any attempt to derive from this class will produce a compile error because the derived class’s constructor cannot call your class’s constructor.

                class NonBase
                {
                public:
                                static NonBase* Create() { return new NonBase (); }

                private:
                                NonBase();

                };

                class Derived : public NonBase {};

                Derived d; // compile error!

  • The downside of this approach is that instances of NonBase cannot be created on the stack. Your clients must instead always allocate instances of NonBase using the NonBase::Create() static function. If this is undesirable, there is another solution. You can instead rely on virtual inheritance to ensure that no concrete class can inherit from NonBase

                class NonBase;
                class NonBaseFinal
                {
                private:
                                NonBaseFinal() {}

                                friend class NonBase;

                };

                class NonBase :virtual public NonBaseFinal
                {
                public:

                                void methodA();
                };

                class Derived : public NonBase {};
                Derived d; // compile error!


References 

  • API Design for C++ Book by Martin Reddy

No comments:

Post a Comment

LeetCode C++ Cheat Sheet June

🎯 Core Patterns & Representative Questions 1. Arrays & Hashing Two Sum – hash map → O(n) Contains Duplicate , Product of A...