Object-Oriented Programming-class scopes under Inheritance
Introduction:
In the case of inheritance,The scope of the derived class is nested in the scope of the base class.: If the name cannot be determined in the scope of the derived class, search for the definition of the name in the scope of the peripheral base class.
The hierarchical nesting of these scopes enables usMembers of the base class can be directly accessed.As if these members are members of a derived class:
Bulk_item bulk; cout << bulk.book() << endl;
The usage of the name book will be determined as follows:
1) bulk is a Bulk_item class object. It can be found in the Bulk_item class and cannot find the name book.
2) Because Bulk_item is derived from Item_base, search for it in the Item_base class and find the name book. The reference is successful.
1. Name Search occurs during compilation
The static types of objects, references, or pointers determine the actions that an object can perform. Even whenThe static and dynamic types may be different.When using a base class type reference or pointer, the static type still determines what Members can be used:
class Disc_item : public Item_base{public: std::pair
discount_policy() const { return std::make_pair(quantity,discount); } //other member as before...};
You can only access discount_policy () through objects, pointers, or references of the Disc_item or Disc_item-derived type ():
Bulk_item bulk; Bulk_item *bulkP = &bulk; Item_base *itemP = &bulk; bulkP -> discount_policy(); //OK itemP -> discount_policy(); //Error
Access through itemP is incorrect, because pointers (references or objects) of the base class type can only access the base class part of the object, but cannot access the derived class part, the discount_policy () member is not defined in the base class.
// P498 exercise 15.21/22 class Item_base {public: Item_base (const std: string & book = "", double sales_price = 0.0): isbn (book), price (sales_price) {} std: string book () const {return isbn;} // only returns the total price. virtual double net_price (std: size_t n) is not discounted) const {return n * price;} virtual ~ Item_base () {} private: std: string isbn; protected: double price;}; class Disc_item: public Item_base {public: Disc_item (const std :: string & book = "", double sales_price = 0.0, std: size_t qty = 0, double disc_rate = 0.0): Item_base (book, sales_price), quantity (qty ), discount (disc_rate) {} // set the function to a pure virtual function to prevent the user from creating the Disc_item object double net_price (size_t) const = 0; std: pair
Discount_policy () const {return std: make_pair (quantity, discount);} protected: std: size_t quantity; // quantity of discounts that can be performed double discount; // discount rate }; // batch purchase discount class Bulk_item: public Disc_item {public: Bulk_item (const std: string & book = "", double sales_price = 0.0, std: size_t qty = 0, double disc_rate = 0.0): Disc_item (book, sales_price, qty, disc_rate) {} double net_price (std: size_t cnt) const {if (cnt> = quantity) {return cnt * (1-discount) * price;} else {return cnt * price ;}}; // class Lds_item: public Disc_item {public: lds_item (const std: string & book = "", double sales_price = 0.0, std: size_t qty = 0, double disc_rate = 0.0): Disc_item (book, sales_price, qty, disc_rate) {} double net_price (std: size_t cnt) const {if (cnt <= quantity) {return cnt * (1-discount) * price ;} else {return price * (cnt-quantity * discount );}}};
Ii. Name Conflict and inheritance
A derived class member with the same name as a base class member will block direct access to the base class member:
Class Base {public: Base (): mem (0) {} protected: int mem;}; class Derived: public Base {public: Derived (int I): mem (I) {} int get_mem () const {return mem; // Derived: mem} private: int mem; // The Base: mem} is blocked };
The reference to mem in get_mem is determined as the name in Derive:
Derived d(43); cout << d.get_mem() << endl; //output 43
You can use the scope operator to access blocked members:
Class Derived: public Base {public: int get_mem () const {return Base: mem; // Derived: mem} // As before }; // test Derived d (43); cout <d. get_mem () <endl; // output 0
The scope operator instructs the compiler to search for mem members in the Base.
[Best practices]
When designing a derived class, if possible,It is best to avoid conflicts with the names of base class members.!
// P499 exercise 15.23 class Base {public: void foo (int); protected: int bar; double foo_bar ;}; class Derived: public Base {public: void foo (string ); bool bar (Base * pb); void foobar (); protected: string bar;}; void Derived: foobar () {bar = "1024";} bool Derived :: bar (Derived * pb) {return foo_bar = pb-> foo_bar;} int main () {Derived d; d. foo ("1024");}/* Note: The g ++ compiler may strictly check the type. This program cannot be compiled on the g ++ compiler, * Because the string ba in Derivd In r, the compiler prompts: it is in conflict with the previous declaration! * Indeed, in Derivd, bar has both data members and member functions !!! */
Iii. Scope and member functions
A member function with the same name is used in the base class and derived class, and its behavior is the same as that of the data member:In the scope of a derived class, the derived class member will be blocked from the base class member.. The base class members are blocked even if the function prototype is different:
Struct Base {int memfuc () ;}; struct Derived: Base {int memfuc (int) ;}; int main () {Derived d; Base B; B. memfuc (); // call Base: memfuc () d. memfuc (10); // call Derived: memfuc () d. memfuc (); // Error d. base: memfuc (); // call Base: memfuc ()}
The memfuc declaration in Derived hides the Declaration in Base. When determining the following statement:
d.memfuc();
The compiler finds the name memfuc and finds it in the Derived class. Once the name is found, the compiler will not continue searching.
[Careful mine]
The functions declared in the local scope do not reload the functions defined in the global scope. Similarly, the functions defined in the derived class do not reload the Members defined in the base class. When calling a function through a derived class object, the real parameter must match the version defined in the derived class, only in the derived classWhen this function is not defined at all. For example:
struct Base{ int memfuc();};struct Derived : Base{ int memfuc(int);};Derived d;d.memfuc();//Error
If you comment out intmemfuc (int) in Derived, then:
d.memfuc();//OK
Overload Functions
Like any other function,Member Functions(Whether virtual or non-virtual)You can also load. A derived class can redefine zero or more inherited versions.
[Note] If the derived class re-defines the overload member, the derived type is used.You can only access the members that are defined in the derived class.!
struct Derived : Base{ int memfuc(); int memfuc(int); double memfuc(double);};int main(){ Derived d; d.memfuc(); //Derived::memfuc() d.memfuc(10); //Derived::memfuc(int)}
If a derived class wants to use all the overloaded versions of its own type, the derived class must either redefine all the overloaded versions or one of them.
Sometimes the class only needsRedefinition of an overloaded versionAndMeaning of other versions to be inheritedIn this case, the derived class does not need to redefine each inherited base class version. It can provide the using Declaration for the overloaded members. A using Declaration can only specify one name, but cannot specify the form parameter table. Therefore, the using declaration willTheAll Heavy Load instancesAdded to the scope of the derived class.After all the names are added to the scope, the derived class only needs to redefine the functions that must be defined for this type, and the inherited definitions can be used for other versions.
Struct Base {int memfuc (); int memfuc (int); int memfuc (double) ;}; struct Derived: Base {using Base: memfuc; int memfuc (); // redefinition}; int main () {Derived d; d. memfuc (); // Derived: memfuc () d. memfuc (10); // Base: memfuc (int )}
Iv. virtual functions and scopes
Virtual function: if the base class member and the derived class member acceptDifferent real parametersThere is no way to call the function of the derived class through the reference or pointer of the base class type:
Class Base {public: virtual int fcn () ;}; class D1: public Base {public: // This fcn shields the virtual function fun int fcn (int) in the Base class ); /** at this time, there are two functions named fcn: * a virtual function named fcn inherited from the Base * class defines a non-virtual member function named fcn, this function accepts an int parameter */}; class D2: public D1 {public:/** redefinition of the two functions it inherits: * 1. redefines the original fcn version defined in the Base * 2. the non-virtual version defined in D1 is redefined. */Int fcn (); int fcn (int );};
Use a base class to call blocked virtual functions
When a function is called through a reference or pointer of the base class type, the compiler searches for the function in the base class and ignores the derived class:
Base bobj; D1 d1obj; D2 d2obj; Base * bp1 = & bobj, * bp2 = & d1obj, * bp3 = & d2obj; bp1-> fcn (); // call Base:: fcn () bp2-> fcn (); // call Base: fcn () bp3-> fcn (); // call D2: fcn ()
[Key concepts: Name Search and inheritance]
The key to understanding the hierarchy of inheritance in C ++ is to understand how to determine function calls. To determine whether a function is called, follow these four steps:
1) First, determine the static type of the object, reference, or pointer for the function call.
2) Search for a function in this class. If the function cannot be found, search for the function in the base class,This followsClass inheritance chainSearch up,Wait until you find the function or find the last class. If the name cannot be found in the class or its related base class, the call is incorrect.
3) once the name is foundRegular type checkTo check whether the function call is valid if the defined value is found.
4) assuming that the function call is valid, the compiler generates code. If the function is a virtual function and called by reference or pointer, the compiler generates code to determineDynamic Object TypeWhich function version is running? Otherwise, the compilerGenerate code to call functions directly.
// P502 exercise 15.24 Bulk_item bulk; Item_base item (bulk); Item_base * p = & bulk;/** because net_price is a virtual function * for virtual functions, virtual functions can only be called through pointer or reference for dynamic binding * and through objects. The called function is always defined in the type of the object */p-> net_price (10 ); // call the net_price item.net _ price (10) of the Bulk_item version; // call the net_price of the Item_base version.