面向对象编程
--纯虚函数、容器与继承
I.纯虚函数
在函数形参后面写上 =0 以指定纯虚函数:
- class Disc_item : public Item_base
- {
- public:
- double net_price(size_t) const = 0; //指定纯虚函数
- };
将函数定义为纯虚函数能够说明,该函数为后代类型提供了可以覆盖的接口,但是这个类的版本绝不会调用。重要的是,用户将不能创建Disc_item类型的对象。
- Disc_item discount; //Error
- Bulk_item bulk; //OK
- //因为Bulk_item类继承自 Disc_item 类,并且重写了 net_price 函数
【注解】
含有(或继承)一个或多个纯虚函数的类是抽象基类。除了作为抽象基类的派生类的对象组成部分,不能创建抽象类型的对象。
- //P503 习题15.26/27
- 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) {}
- double net_price(size_t) const = 0;
- protected:
- std::size_t quantity;
- double discount;
- };
- //...
- int main()
- {
- Disc_item item;
- }
错误信息:
II.容器与继承
我们希望使用容器(或内置数组)保存因继承而相关联的对象。但是,对象不是多态的,这一事实对将容器用于继承层次中的类型有影响。
如果定义multiset保存基类类型的对象:
- multiset<Item_base> basket;
- Item_base base;
- Bulk_item bulk;
- basket.insert(base); //OK
- basket.insert(bulk); //OK:但是,bulk的派生类部分将被无情的“切割”掉
则加入派生类型的对象时,只将对象的基类部分保存在容器中。谨记:将派生类对象复制到基类对象时,派生类对象将被切掉。因此,一旦放入了multiset,他就不再是派生类对象了。
【小心地雷】
因为派生类对象在赋值给基类对象时会被“切掉”,所以容器与通过继承相关的类型不能很好的融合。
不能通过定义容器保存派生类对象来解决这个问题。在这种情况下,不能将Item_base对象放入容器—— 没有从基类类型到派生类型的标准转换。可以显式地将基类对象强制转换为派生类对象并将结果对象加入容器,但是,如果这样做,当试图使用这样的元素时,会产生大问题:在这种情况下,元素可以当作派生类对象对待,但派生类部分的成员将是未初始化的。
唯一可行的选择可能是使用容器保存对象的指针。这个策略可行,但代价是需要用户面对管理对象和指针的问题,用户必须保证只要容器存在,被指向的对象就存在。如果对象是动态分配的,用户必须保证在容器消失时适当地释放对象。
- //P504 习题15.28
- int main()
- {
- vector<Item_base> itemVec;
- for (size_t i = 0; i != 10; ++i)
- {
- Bulk_item item("C++ Primer",6,5,0.5);
- itemVec.push_back(item);
- }
- double sum = 0;
- for (vector<Item_base>::iterator iter = itemVec.begin();
- iter != itemVec.end(); ++iter)
- {
- sum += iter -> net_price(10); //调用Item_base:: net_price
- }
- cout << sum << endl; //output 600
- }
- //习题15.29
- int main()
- {
- vector<Item_base *> itemVec;
- for (size_t i = 0; i != 10; ++i)
- {
- Bulk_item *item = new Bulk_item("C++ Primer",6,5,0.5);
- itemVec.push_back(item);
- }
- double sum = 0;
- for (vector<Item_base *>::iterator iter = itemVec.begin();
- iter != itemVec.end(); ++iter)
- {
- sum += (*iter) -> net_price(10);
- }
- cout << sum << endl; //output 300
- //显式释放动态分配的内存
- for (vector<Item_base *>::iterator iter = itemVec.begin();
- iter != itemVec.end(); ++iter)
- {
- delete *iter;
- }
- }