Defining Base and Derived Classes

来源:互联网 发布:js 二维数组 indexof 编辑:程序博客网 时间:2024/06/11 21:14

Defining Base and Derived Classes

Defining a Base Class

class Quote {public:Quote() = default; // = default see § 7.1.4 (p. 264)Quote(const std::string &book, double sales_price):bookNo(book), price(sales_price) { }std::string isbn() const { return bookNo; }// returns the total sales price for the specified number of items// derived classes will override and apply different discount algorithmsvirtual double net_price(std::size_t n) const{ return n * price; }virtual ~Quote() = default; // dynamic binding for the destructorprivate:std::string bookNo; // ISBN number of this itemprotected:double price = 0.0; // normal, undiscounted price};

The new parts in this class are the use of virtual on the net_price function and the destructor, and the protected access specifier.
Note: Base classes ordinarily should define a virtual destructor. Virtual destructors are needed even if they do no work.

Member Functions and Inheritance

Because the operation net_price is type dependent, the derived class needs to override the definition it inherits from the base class, by providing its own definition.
The base class defines as virtual those functions it expects its derived classes to override. When we call a virtual function through a pointer or reference, the call will be dynamically bound.
Note:
1. Any nonstatic member function, other than a constructor, may be virtual.
2. The virtual keyword appears only on the declaration inside the class and may not be used on a function definition that appears outside the class body.
3. A function that is declared as virtual in the base class is implicitly virtual in the derived classes as well.

Access Control and Inheritance

A derived class may access the public members of its base class but may not access the private members. A base class specify members after a protected access specifier, so that lets its derived classes use while still prohibiting access to those same members by other users.

Defining a Derived Class

A derived class must specify from which class(es) it inherits. It does so in its class derivation list, which is a colon followed by a comma-separated list of names of previously defined classes. Each base class name may be preceded by an optional access specifier, which is one of public, protected or private.

class Bulk_quote : public Quote { // Bulk_quote inherits from QuoteBulk_quote() = default;Bulk_quote(const std::string&, double, std::size_t,double);// overrides the base version in order to implement the bulk purchase discount policydouble net_price(std::size_t) const override;private:std::size_t min_qty = 0; // minimum purchase for the discount to applydouble discount = 0.0; // fractional discount to apply};

Virtual Functions in the Derived Class

If a derived class does not override a virtual from its base, then, like any other member, the derived class inherits the version defined in its base class.
A derived class may include the virtual keyword on the functions it overrides, but it is not required to do so.
The new standards lets a derived class explicitly note that it intends a member function to override a virtual that it inherits. It do so by specifying override after the parameter list, or after the const or reference qualifier(s) if the member is a const or reference function.

Derived-Class Objects and the Derived-to-Base Conversion

We can bind a base-class reference or pointer to the base-class part of a derived object.

Quote item; // object of base typeBulk_quote bulk; // object of derived typeQuote *p = &item; // p points to a Quote objectp = &bulk; // p points to the Quote part of bulkQuote &r = bulk; // r bound to the Quote part of bulk

This conversion is often referred to as the derived-to-base conversion.

Derived-Class Constructor

A derived class must use a base-class constructor to initialize its base-class part.
A derived-class constructor uses its constructor initializer list to pass arguments to a base-class constructor.
The Bulk_quote constructor with four parameters:

Bulk_quote(const std::string& book, double p,std::size_t qty, double disc) :Quote(book, p), min_qty(qty), discount(disc) { }// as before};

passes its first two parameters(representing the ISBN and price ) to the Quote constructor. That Quote constructor initializes the Bulk_quote’ s base-class part. Next the direct members, min_qty and discount, are initialized.
Note: The base class is initialized first, and then the members of the derived class are initialized in the the order in which they are declared in the class.

Using Members of the Base Class from the Derived Class

// if the specified number of items are purchased, use the discounted pricedouble Bulk_quote::net_price(size_t cnt) const{if (cnt >= min_qty)return cnt * (1 - discount) * price;elsereturn cnt * price;}

The scope of a derived class is nested inside the scope of its base class. As a result, there is no distinction between how a member of the derived class uses members defined in its own class and how it uses members defined in its base.

Inheritance and static Members

If a base class defines a static member, there is only one such member defined for the entire hierarchy. Regardless of the number of classes derived from a base class, there exists a single instance of each static member.

class Base {public:static void statmem();};class Derived : public Base {void f(const Derived&);};

Note: static members obey normal access control. If the member is private in the base class, then derived classes have no access to it.

void Derived::f(const Derived &derived_obj){Base::statmem(); // ok: Base defines statmemDerived::statmem(); // ok: Derived inherits statmem// ok: derived objects can be used to access static from basederived_obj.statmem(); // accessed through a Derived objectstatmem(); // accessed through this object}

Declarations of Derived Classes

Note: The declaration contains the class name but does not include its derivation list:

class Bulk_quote : public Quote; // error: derivation list can't appearhereclass Bulk_quote; // ok: right way to declare a derived class

The derivation list, and all other details of the definition, must appear together in the class body.

Classes Used as a Base Class

A class must be defined, not just be declared*, before we can use it as a base class:

class Quote; // declared but not defined// error: Quote must be definedclass Bulk_quote : public Quote { ... };

The reason for this is, to use these members from base class, the derived class must know what they are. One implication of this rule is that it is impossible to derive a class from itself.

Preventing Inheritance

If we define a class that we don’t want others to inherit from, we can prevent a class from being used as a base by following the class name with final.

class NoDerived final { /* */ }; // NoDerived can't be a base classclass Base { /* */ };// Last is final; we cannot inherit from Lastclass Last final : Base { /* */ }; // Last can't be a base classclass Bad : NoDerived { /* */ }; // error: NoDerived is finalclass Bad2 : Last { /* */ }; // error: Last is final

Conversions and Inheritance

We can bind a pointer or reference to a base-class type to an object of a type derived from that base class.

Static Type and Dynamic Type

The static type of an expression is always known at compile time. The dynamic type may not be known until run time.

There is no Implicit Conversion from Base to Derived

Because a base object might or might not be part of a derived object, there is no automatic conversion from the base class to its derived class(s):

Quote base;Bulk_quote* bulkP = &base; // error: can't convert base to derivedBulk_quote& bulkRef = base; // error: can't convert base to derived

We cannot convert from base to derived even when a base pointer or reference is bound to a derived object:

Bulk_quote bulk;Quote *itemP = &bulk; // ok: dynamic type is Bulk_quoteBulk_quote *bulkP = itemP; // error: can't convert base to derived

The compiler has no way to know (at compile time) that a specific conversion will be safe at run time. The compiler looks only at the static types of the pointer or reference to determine whether a conversion is legal.