"Effective C + +": Clause 32-clause 33

Source: Internet
Author: User

    • Clause 32 determine your public inheritance mold out is-a relationship
    • Article 33 avoid concealing the inherited name

Clause 32-clause 40 is about inheriting relevant content.
Article 32 describes the public inheritance that shapes the relationship between a base class and a derived class.
Clause 33 describes the scope of the variable and the masking relationship in the inheritance hierarchy.

Clause 32: Determine your public inheritance mold out is-a relationship

In C + + object-oriented programming, one of the most important rules is that public inheritance (publicly inherited) means "is-a" (a) relationship. Here is "literal translation", such as class D: public B literal translation is a D is a B.

If you let Class D ("Derived") Inherit Class B ("Base") as public, it means that an object of type D is also an object of type B (or that the D object contains a B object), but not the other way around. B shows a more generalized concept than D, whereas D shows a more specialized concept than B. You can use a B object, or you can use a D object.

Take a specific example to illustrate:

    class Person {……};    class Student: public Person {……};

Life experience can tell that every student is a person, but not everyone is a student. This is the idea of this succession system. It can be expected that every thing that is set up for a person is also set up for the students. However, the establishment of students may not be set up for people. In C + +, any function that expects to accept an argument of the type Person object (or pointer to person, or reference to person) can also accept an argument of the type Student object (or pointer to student, or reference to student).

eat(const Person& p);    study(const Student& s);    p;    Student s;    eat(p);//正确    eat(s);//正确    study(s);//正确    study(p);//错误 

Of course, the above-mentioned correct premise is to inherit with public, if the private inheritance, the meaning will be completely different (* * clause **39), protected inheritance is the same.

Sometimes the relationship between public and is-a can mislead us. For example, a Penguin (Penguin) is a bird, and it is true that birds can fly, which is also true. If you describe this layer of relationships in C + +:

    class Bird{        public:        virtualvoidfly();        ……    };    public Bird{        ……    };

But we know that penguins can't fly, that's the truth. The reason for this problem is that language (English) is not rigorous. When we say that birds can fly, we express the meaning that ordinary birds will fly, not all birds will fly. We should also admit the fact that some birds can't fly. This will shape the inheritance relationship.

    class Bird{        ……    };    public Bird{    public:        voidfly();        ……    };    public Bird{//没有fly函数        ……    };

Such a design can better reflect the meaning we really want to express. But at this point, we are still not fully disposed of these birds. For example, if your system does not differentiate between birds and birds, you are concerned with bird pecking and bird wings, so the original "dual Class inheritance system" is better suited to your system. There is no perfect design, specific issues to be discussed in detail.

There is also a way to deal with "All birds will fly, penguins are birds, but penguins do not fly" This problem, we can redefine the fly function in the Penguin class, let it produce a run-time error:

    void error(const std::string& msg);//输出错误    public Bird {    public:        virtualvoidfly(){ error("Attemp to make a penguin fly");}        ……    };

Here the solution is different, here does not say penguins can not fly, when you say penguins will fly, will tell you this is a mistake. But what is the difference between this workaround? From the time the error was detected, the first solution, "Penguins do not fly", imposes the fact that the restriction is imposed at compile time; the second solution, "Penguins fly is wrong" is detected at run time. The first solution is better, * * clause **18 said that good interfaces can prevent invalid code from being compiled, by contrast, we should choose to find this problem in the compile time.

Considering an example of basic geometry we've all learned, then how complex is the relationship between squares and rectangles? Let's look at the following example: Class square should be based on the public form class rectangle? We all know that a square is a special rectangle, if you inherit from public

Class rectangle{ Public:Virtual void setheight(intNewheight);Virtual voidSetWidth (intNewwidth);Virtual intHeight ()Const;Virtual intWidth ()Const; ...... };voidMakebigger (rectangle& R)//Increase the area of R{intOldheight=r.height; R.setwidth (R.width () +Ten);//r Width IncreaseASSERT (R.height () ==oldheight);//Determine if the height of R is changed}

The above assert result must be true because it makeBigger only changes the width of R and the height does not change.

    Square:public Rectangle{……};    Square s;    ……    asseret(s.width()==s.height());//对所有正方形都为真    makeBigger(s);//因为是public继承是is-a关系,所以可以使用这个函数    asseret(s.width()==s.height());//对正方形也应该为真

Well, there must be a problem now. Since the first assert, the length and width are equal, then the width is increased, the length is unchanged, and the length and width are equal when the second assert is reached.

As I said earlier, every thing that can be performed on a base class object, inherited by public, is performed on the derived object. In the case of the positive rest and the rectangle (there is also a similar to clause 38 of sets and lists), this conclusion does not work, so a public inheritance mold between them is incorrect. So we should keep in mind that the code can run correctly by compiling it.

Is-a is just one of the class inheritance relationships, and there are two inheritance relationships has-a(with one) and is-implemented-in-terms-of(implemented according to something). These relationships will be discussed in terms **38 and terms **39. When designing a class, you should understand the relationship between these classes and the interaction between them, and the relationships between the mold classes.

Summary
-"public" inheritance means is-a. For base classes everything must also apply to derived, because each derived object is also a base class object.

Article 33: Avoid hiding the inherited name

The name mentioned here is related to inheritance and scope. Let's take a look at a scope-related example:

int x;//global    void someFunc();    {        double x;//local        std::cin>>x;//给local变量赋值    }

This CIN is assigned to the local variable x instead of the global variable x because the inner domain name obscures the perimeter scope names. When the compiler encounters the name X in the SomeFunc scope, it looks for the variable definition within the local scope, and then finds the other scope if it is not found. In this example, the variable x type is different, the local is a double type, and global is the int type; but this doesn't matter, the only thing that the C + + name masking rule (name-hiding rules) does is to obscure the name, and the type doesn't matter.

Now take a look at inheritance. When a derived class member function refers to something within the (refer to) base class (member function, member variable, typedef, and so on), the compiler can find what it refers to, because derived class inherits the declaration in the base Everything inside the class. The scope of the derived class is nested within the base class scope.

 class base{private :        int  x; public : virtual              void  mf1  () =0 ; virtual             void  mf2 ();            void  mf3 ();        ...... };            Class Derived: public  base{public : virtual  void  mf1             ();            void  mf4 (); ...};  


This example has both public and private. The member functions have pure virtual, impure virtual, and non-virtual, which is to emphasize that we are talking about names, and nothing else. This example is a single inheritance, and understanding single inheritance can easily infer multiple inheritance. Assume that Mf2 is called within the mf4 of the derived class

void Derived::mf4()    {        mf2();    }

When the compiler sees MF2, know what it refers to (refer to). Find something with the name Mf2 first in the local scope (that is, the scope covered by the MF4), if you can't find it, then look for the perimeter scope (the scope of class derived coverage), and if you haven't found it, go to the perimeter (the base class coverage scope) and find it here. If the base is still not found, then continue to find it within the base namespace scope and finally to the global scope.

The following example becomes slightly more complicated, overloading mf1 and mf3, and adding a new version of Mf3 to derived.

Class base{Private:intX Public:Virtual void MF1()=0;Virtual voidMF1 (int);Virtual voidMf2 ();voidMF3 ();voidMF3 (Double);    ...... }; Class Derived: Publicbase{ Public:Virtual void MF1();voidMF3 ();voidMF4 (); ...... };


Because of the scope-based "name masking rules", all functions named MF1 and MF3 within the base class are obscured by the MF1 and MF3 functions within the derived class. From the name lookup standpoint, BASE::MF1 and BASE::MF3 are no longer inherited by derived.

 Derived d;    int x;    d.mf1();//正确,调用Derived::mf1    d.mf1(x);//错误,因为Derived::mf1遮掩了Base::mf1    d.mf2();//正确,调用Base::mf2    d.mf3();//正确,调用Derived::mf3    d.mf3(x);//错误,因为Derived::mf3遮掩了Base::mf3

clause **32 said that public inheritance is a **is-a relationship, and if you use public inheritance without inheriting those overloaded functions, it violates the is-a relationship. The function calls above are correct, but using the use declaration

Class base{Private:intX Public:Virtual void MF1()=0;Virtual voidMF1 (int);Virtual voidMf2 ();voidMF3 ();voidMF3 (Double);    ...... }; Class Derived: Publicbase{ Public://Let baseclassEverything named Mf1 and MF3 is visible within the derived scope and is Public        usingBASE::MF1;usingBASE::MF3;Virtual voidMF1 ();voidMF3 ();voidMF4 (); ...... };

In this way, the following calls are not faulted.

 Derived d;    int x;    d.mf1();//正确,调用Derived::mf1    d.mf1(x);//调用Base::mf1    d.mf2();//正确,调用Base::mf2    d.mf3();//正确,调用Derived::mf3    d.mf3(x);//调用Base::mf3

If you inherit base class and add overloaded functions, and you want to redefine or overwrite some of them, introduce each name that is obscured to a using declaration.

Public inheritance implies a is-a relationship between base and derived class, which is why the above using declaration is placed within the public scope of the derived class: base The public name within class should also be public within the publicly derived class.

If you want private to inherit base, and derived only want to inherit that version of MF1 without parameters in base, the using declaration is not useful in this way, because the using declaration makes the inherited name of all functions visible in the derived class. Such implementations require a different technique, a simple handoff function (forwarding functions):

 class base{public : virtual  void  mf1  ()        =0 ; virtual     void  mf1 (int );    }; Class Derived: private  base{public : virtual  void  MF1  ()/ /Transfer function (forwarding functions) {BASE::MF1 ();};    //implicitly becomes inline };    Derived D;    int  x; D.MF1 (); //call derived::mf1  d.mf1 (x); //error, BASE::MF1 is obscured   

All of the above are not included in the templates. When inheritance is combined with templates, it is also faced with "the succession name is obscured", and the "angle bracket delimitation" thing, in the * * clause **43 discussed.

Summary
-The name inside the derived classes will obscure the name within the base classes. No one has ever wished to do so under public inheritance.
-In order for the masked name to be derived within the class, you can use the using declaration or the transfer function (forwarding functions).

"Effective C + +": Clause 32-clause 33

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.