!!!Chapter 15 Object-Oriented Programming (15.6 ~ 15.8)

来源:互联网 发布:淘宝手机端删除评价 编辑:程序博客网 时间:2024/04/28 09:36

15.6 Pure Virtual Functions

If we want certain virtual function cannot be used in some classes, we can making it apure virtual function.E.G. P596

A pure virtual function is specified by writing = 0 after the function parameter list:

class Disc_item : public Item_base {public:    double net_price (std::size_t) const = 0;};
Defining a virtual as pure indicates that the function provides an interface for subsequent types to override but that the version in this class will never be called.

As importantly, uses will not be able to create objects of type Disc_item.

// Disc_item declares pure virtual functionsDisc_item discounted;    // error: cannot define a Disc_item objectBulk_item bulk;              // ok: Disc_item subobject within Bulk_item
A class containing (or inheriting) one or more pure virtual functions is anabstract base class.We may not create objects of an abstract type except as parts of objects of classes derived from the abstract base.   ???

15.7 Containers and Inheritance

When we use a derived type object to a base container, the derived object will be sliced down:

multiset<Item_base> basket;Item_base base;Bulk_item bulk;basket.insert(base);    //ok: add copy of base into basketbasket.insert(bulk);    //ok: but bulk sliced down to its base part
Once the derived object is put into the multiset, it is no longer a derived object:

The only viable alternative would be to use the container to hold pointers to our objects.

15.8 Handle Classes and Inheritance

To use dynamic binding, we must rely on reference and pointer, which will pus a burden on the users of classes.

A common technique in C++ is to define a so-called cover or handle class. The handle classes stores and manages a pointer to the base class.

Handles that cover an inheritance hierarchy have two important design considerations:

1. As with any class that holds a pointer, we must decide what to do about copy control. Handles behave like either smart pointer or a value.

2. The handle class determines whether the handle interface will hide the inheritance hierarchy or expose it. If the hierarchy is not hidden, users must know about and use objects in the underlying hierarchy.

15.8.1 A Pointerlike Handle

Sales_item is pointing to Item_base hierarchy:

// bind a handle to a Bulk_item objectSales_item item(Bulk_item("0-201-8724-1", 35, 3, .20));item->net_price();   //virtual call to net_price function

Defining the Handle

The handler class have three constructors: default constructor, copy constructor, constructor takes an Item_base.

The Sales_item class will have two data members, both of which are pointers. One pointer will point to the Item_base object and the other will point to the use count.P 600

// Use counted handle class for the Item_base hierarchyclass Sales_item {public:    // default constructor: unbound handle    Sales_item() : p(0), use(new std::size_t(1)) {}    // attaches a handle to a copy of the Item_base object    Sales_item (const Item_base&);    // copy control members to manage the use count and pointers     Sales_item (const Sales_item &i) : p(i.p), use(i.use) { ++*use;}    ~Sales_item() {decr_use();}    Sales_item& operator=(const Sales_item&);    // member access operators    const Item_base *operator->() const { if(p) return p;        else throw std::logic_error("unbound Sales_item");}    const Item_base &operator*() const ( if (p) return *p;        else throw std::logic_error("unbound Sales_item");} private:    Item_base *p;    std::size_t *use;    // called by both destructor and assignment operator to free pointers    void decr_use()        { if (--*use == 0) {delete p; delete use;}}     // use will minus 1};

Use-Counted Copy Control

Assignment operator:

Sales_item& Sales_item::operator= (const Sales_item &rhs){    ++rhs.use;    decr_use();    p = rhs.p;    use = rhs.use;    return *this;}

15.8.2 Cloning an Unknown Type

To implement the constructor that takes an Item_base, we must solve a problem: we don't know the actual type of the object that the constructor is given.

Handle classes often need to allocate a new copy of an existing object without knowing the precise type of the object.

The common approach to solving this problem is to define a virtual operation to do the copy, which we'll nameclone.

To support our handle class, we need to add clone to each hierarchy class. Start from the base class:

class Item_base {public:    virtual Item_base* clone() const            { return new Item_base(*this); }};
Each derived class also need to redefine the virtual(exception ofP 564):

class Bulk_item : public Item_base {public:    Bulk_item* clone() const            { return new Bulk_item(*this); }};

Defining the Handle Constructors

Sales_item::Sales_item (const Item_base & item) :        p(item.clone()), use(new std::size_t(1)) {}

15.8.3 Using the Handle(use comparator with an associative container)

When we want to store Sales_item objects in a multiset, and we don't want to define operator< for Sales_item class. So we must rely on comparator.

First, we need to define a function to compare Sales_item objects:

// compare defines item ordering for the multiset in Basketinline bool compare (const Sales_item &lhs, const Sales_item &rhs){    return lhs->book() < rhs->book());}
To use our Sales_item comparision, we must specify the comparator type when we define the multiset. We start by defining a typedef that is a synonym for this type:

// type of the comparison function used to order the multisettypedef bool (*Comp) (const Sales_item&, const Sales_item&);// Here Comp is a synonym for the pointer to function type that matches the comparison function
Next we define a multiset that holds objects of type Sales_item and that uses this Comp type for its comparison function:

std::multisetM<Sales_item, Comp> items(compare);
Example: P 605

原创粉丝点击