Effective C ++ Item 34: Inheritance of the object-oriented (to distinguish interface inheritance and implementation inheritance)

First, the treatment of inherited interface

  • As a class designer, for the base class member function can be treated substantially following three ways:
    • ① pure virtual function: base class defines a pure virtual function, then let the derived class to implement
    • ② pure virtual virtual non-virtual function: base class defines a pure virtual virtual non-virtual function, derived classes to override and allow coverage (the override)
    • ③ Common member function: base class defines a generic member function, derived classes and do not want to hide
  • This article describes the order of these three design principles above . The following definition of a class as a basis to explain herein:
class Shape {
public:
    virtual void draw()const = 0; //纯虚函数
    virtual void error(const std::string& msg); //非纯虚函数
    int objectID()const; //普通成员函数
};

class Rectangle :public Shape {};
class Ellipse :public Shape {};

Two, pure virtual function

  • This is the first case mentioned at the beginning of the article: derived classes inherit only the interface (pure virtual function) member function of the base class, the derived class to realize his pure virtual function
  • Some features pure virtual function:
    • ① has a pure virtual function in the class can not be instantiated
    • ② has a pure virtual function of class, derived classes must implement the pure virtual function
  • In the above, our pure virtual function is:
class Shape {
public:
    virtual void draw()const = 0; //纯虚函数
};

class Rectangle :public Shape {};
class Ellipse :public Shape {};
  • Wherein the object is a pure virtual function involving:
    • Shape pattern is a base class for all classes which provide a draw () function of the drawing, but due to its derived class (rectangle, circle, etc.) of the drawing is not the same manner, and therefore can not be () function provides a default draw the default behavior, the Shape draw () is defined as a pure virtual function, derived classes to automatically let
  • Case presentation:
class Shape {
public:
    virtual void draw()const = 0;
};

class Rectangle :public Shape {
public:
    virtual void draw()const {
        std::cout << "Rectangle" << std::endl;
    }
};
class Ellipse :public Shape {
public:
    virtual void draw()const {
        std::cout << "Ellipse" << std::endl;
    }
};

int main()
{
    //Shape *ps = new Shape;    //错误,不能实例化
    Shape *ps1 = new Rectangle;
    Shape *ps2 = new Ellipse;

    ps1->draw(); //调用Rectangle::draw()
    ps2->draw(); //调用Ellipse::draw()

    return 0;
}
  • While the limited-use pure virtual function, it can realize a mechanism for non-pure virtual virtual virtual function to provide a more secure usual default implementation (see description below)

Third, non-pure virtual virtual virtual function

Case presentation (presentation advantages)

  • First look at a presentation case virtual function
  • Suppose a company to design an airplane aerospace succession system, the company now only A and B both aircraft, as follows:
class Airport {}; //机场

class Airplane {  //飞机的基类
public:
    virtual void fly(const Airport& destination) {
        //飞机飞往指定的目的地(默认行为)
    }
};

//A、B两个派生类
class ModelA :public Airplane {};
class ModelB :public Airplane {};
  • fly () function is declared as virtual functions because A and B have the same default two aircraft flying behavior, and therefore () function is defined in this default behavior in the fly Airplane flight class, and then let the A and B inherit. Such benefits are:
    • All properties moved to the base class, and then let the two class inheritance
    • Avoiding code duplication, strengthen and enhance the future ability to slow down the long-term maintenance costs required

Case presentation (introduction to pure virtual functions instead of virtual functions)

  • The above A and B has a default flight behavior, so the Airport's fly () function is defined in this default behavior
  • But if the aerospace company added a C-type aircraft, as follows:
class ModelC :public Airplane {};
  • If C aircraft flight may not be suitable default behavior, you might mistakenly call Airplane in fly () function, for example:
Airport PDX;

//C型飞机不具有fly()的行为,但是却调用了fly()
Airplane *pa = new ModelC;
pa->fly(PDX);
  • This is the concept of design errors, we can modify the code, cut off the connection between the "virtual function" and its "default implementation." code show as below:
class Airport {}; //机场
class Airplane {
public:
    virtual void fly(const Airport& destination) = 0;
protected:
    void defaultFly(const Airport& destination) {
        //飞机飞往指定的目的地(默认行为)
    }
};

class ModelA :public Airplane {
public:
    virtual void fly(const Airport& destination) {
        defaultFly(destination);
    }
};
class ModelB :public Airplane {
public:
    virtual void fly(const Airport& destination) {
        defaultFly(destination);
    }
};

class ModelC :public Airplane {
public:
    virtual void fly(const Airport& destination) {
        //C型飞机不可以使用默认飞行行为,因此定义自己的飞行方式
    }
};
  • Now C aircraft or other aircraft do not accidentally add inherit the default behavior of the flight (flight because we will be the default behavior of the package to a function in the defualtFly), you can define the behavior of the fly in fly in
  • Note that in the fly A and Class B of () function, for defaultFly () function made a call to inline (see Item 30, the interaction between inline and virtual functions)

Case presentation (pure virtual function instead of the virtual function of another implementation)

  • Above we will fly () interface and implementation (defaultFly () function) separately to achieve, some people may be against this, because it would duplicate the function names due to excessive class namespace pollution caused
  • If you do not want to separate these two acts, it may be defined as a pure virtual function, in which a given defaultFly () function content. E.g:
class Airport {}; //机场
class Airplane {
public:
    //实现纯虚函数
    virtual void fly(const Airport& destination) = 0 {
        //飞机飞往指定的目的地(默认行为)
    }
};

class ModelA :public Airplane {
public:
    virtual void fly(const Airport& destination) {
        Airplane::fly(destination);
    }
};
class ModelB :public Airplane {
public:
    virtual void fly(const Airport& destination) {
        Airplane::fly(destination);
    }
};

class ModelC :public Airplane {
public:
    virtual void fly(const Airport& destination) {
        //定义自己的飞行方式
    }
};
  • The design and implementation of the above functions and demo cases is the same, but the fly in the derived class () function with a pure virtual function Airplane :: fly replace the independent function Airplane :: defaultFly. Such mergers lost "Let the two functions enjoy different levels of protection" opportunities: for example, the above defaultFly () function becomes protected from the public

Fourth, the ordinary member functions

  • Finally, take a look at the ordinary member functions Airplane
class Shape {
public:
    int objectID()const; //普通成员函数,不希望派生类隐藏
};

class Rectangle :public Shape {};
class Ellipse :public Shape {};
  • Purpose ordinary member functions:
    • Means that the base class does not want to hide the derived class member function
    • In fact, an ordinary member function of the performance of the immutability override its specificity, because it shows that regardless of the derived class becomes Duo Tete alienation, its behavior can not change
  • In the above code:
    • Shape Each object has a function for generating an object identification code
    • This identifier is always the same calculation method, which has Shape :: objectID definition equation determined, any derived classes should not attempt to change the behavior
  • Since the meaning of such functions on behalf of ordinary members of the immutability override is specific, so it should never be redefined in a derived class (which is also the focus of a discussion of the terms of 36)

Five, class design often make two mistakes

  • The difference between the "pure virtual function, non-pure virtual virtual virtual function, ordinary member function" that specify what you want derived class inheritance, for different functions, inexperienced class designers most commonly committed the following two error

The first error

  • The first mistake is to declare all functions as "an ordinary member function" , which makes the derived class meters extra room for specialized work
  • In particular, non-virtual destructor cause problems (see Item 7)
  • Of course, if a class is not intended as a base class, then all functions declared as "ordinary member function" is possible. But if the class will be a base class, you can declare an appropriate number of virtual functions (see clause 7)
  • If you look out the cost of virtual functions, you can see the 80-20 rule (see also clause 30):
    • The rule is: a typical program, 80% of the execution time is spent in 20% of the code body
    • This rule means that, on average, your function call can have 80% efficiency without substantially impact the program virtual. So when before you worry about the cost of virtual function, first focus on that decisive 20% of the code, it is the real key

The second error

  • The second error is that all members of the function is declared as virtual
  • Sometimes rightly so, for example, in terms of Interface classes 31. However, some functions that should not be redefined in a derived class, so you should be declared as a function of those of non-virtual

VI Summary

  • Interface inheritance and implementation inheritance different. Under public inheritance, derived classes always inherit base class interface
  • pure virtual function only specific hiding interface inheritance
  • Simple (impure) impure virtaul function specify the default interface inheritance and implementation inheritance
  • specify non-virtual function interface inheritance and mandatory inheritance
Released 1504 original articles · won praise 1063 · Views 430,000 +

Guess you like

Origin blog.csdn.net/qq_41453285/article/details/104785357