C++ Primer 学习笔记_67_面向对象编程 --转换与继承、复制控制与继承(二)

2014-11-24 12:50:36 · 作者: · 浏览: 5
初始化的![派生类]



2、定义默认构造函数

因为Bulk_item具有内置类型成员,所以应定义自己的默认构造函数:

class Bulk_item:public Item_base
{
public:
    Bulk_item():min_qty(0),discount(0) {}
    // AS Before...
};

该构造函数出了初始化qty和discount成员之外,还会隐式调用Item_base的默认构造函数初始化对象的基类部分[其实是先运行基类的默认构造函数的!]

运行这个构造函数的效果是,首先使用Item_base的默认构造函数初始化Item_base部分,Item_base的构造函数执行完毕后,再初始化Bulk_item部分的成员执行构造函数的函数体(函数体为空)。



3、向基类构造函数传递实参

派生类构造函数初始化列表只能初始化派生类的成员,不能直接初始化继承成员。相反,派生类构造函数通过将基类构造函数包含在初始化列表中来间接初始化继承成员:

    Bulk_item(const std::string &book,double sales_price,
              std::size_t qty = 0,double disc_rate = 0):
        Item_base(book,sales_price),
        min_qty(qty),discount(disc_rate) {}

这个构造函数可以这样使用:

    Bulk_item bulk("0-201-82470-1",50,5,.19);

要建立bulk:

1)首先运行Item_base构造函数,该构造函数使用Bulk_item构造函数初始化列表来传递来的实参初始化isbn和price。

2)初始化Bulk_item的成员。

3)运行Bulk_item的构造函数(空)函数体。

【小结】

构造函数初始化列表为类的基类和成员提供初始值,它并不指定初始化的执行次序。首先初始化基类,然后根据声明次序初始化派生类的成员



4、在派生类构造函数中使用默认实参

可以将这两个Bulk_item构造函数编写为一个接受默认实参的构造函数:

class Bulk_item:public Item_base
{
public:
    Bulk_item(const std::string &book = "",double sales_price = 0.0,
              std::size_t qty = 0,double disc_rate = 0):
        Item_base(book,sales_price),
        min_qty(qty),discount(disc_rate) {}
    //AS Before
};

5、只能初始化直接基类

一个派生类类只能初始化它自己的直接基类,直接基类就是在派生列表中指定的类。

可以设置一个折扣策略需要一个数量和一个折扣量,可以定义名为Disc_item的新类存储数量和折扣量,以支持这些根据购买量来打折的折扣策略。Disc_item类可以不定义net_price函数,但可以作为定义不同折扣策略的其他类(如Bulk_item类)的基类。



【关键概念:重构】

将Disc_item加到Item_base层次是重构的一个例子。重构包括重新定义类层次将操作和/或数据从一个类移到另一个类。为了适应应用程序的需要重新设计类以便增加新的函数或处理其他改变时,最有可能需要进行重构。

重构在面向对象应用程序中非常常见。值得注意的是:虽然改变了继承层次,使用Bulk_item或Item_base类的代码不需要改变。然而,对类进行重构,或以任意其他方式改变类,使用这些类的任意代码都必须重新编译



要实现这个设计:

1)首先需要定义Disc_item类:

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) {}

protected:
    std::size_t quantity;
    double discount;
};

2)其次,可以重新实现Bulk_item以继承Disc_item,而不再是直接继承Item_base:

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) {}

    //重定义根类的Item_base::net_price
    double net_price(std::size_t ) const;
};

现在,每个Bulk_item对象有三个子对象:一个(空的)Bulk_item部分、一个Disc_item子对象,Disc_item子对象又有一个Item_base基类子对象。

由于派生类构造函数只能初始化自己的直接基类,因此在Bulk_item类的构造函数初始化列表中指定Item_base是一个错误。



【关键概念:尊重基类接口

构造函数只能初始化其直接基类的原因是每个类都定义了自己的接口。定义Disc_item时,通过定义它的构造函数指定了怎样初始化Disc_item对象。一旦类定义了自己的接口,与该类对象的所有交互都应该通过该接口,即使对象是派生类对象的一部分也不例外

同样,派生类构造函数不能初始化基类的成员不应该对基类成员赋值。如果那些成员为 public或protected,派生构造函数可以在构造函数函数体中给基类成员赋值,但是,这样做会违反基类的接口。派生类应通过使用基类构造函数尊重基类的初始化意图,而不是在派生类构造函数函数体中对这些成员赋值。

//P493 习题15.14
class Item_base
{
public:
    Item_base(const std::string &book = "",
              double sales_price = 0.0):
        isbn(book),price(sales_price) {}

private:
    std::string isbn;
    double price;
};

class Bulk_item : public Item_base
{
public:
    Bulk_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),
        min_qty(qty),discount(disc_rate) {}

private:
    std::size_t min_qty;
    double discount;
};


//习题15.16
//1)