C++ 面向对象编程(1)
在<<c++ primer>>第四版的第15章中详细讲解了类的定义,继承关系,多态。这里主要实现这一章中的例子。
首先看定义Item_base对象,文件为Item_Base.h。
1 #pragma once 2 #ifndef __ITEMBASE__ 3 #define __ITEMBASE__ 4 5 #include <string> 6 #include <iostream> 7 8 class Item_base 9 { 10 public: 11 Item_base(const std::string &book="",double sales_price=0.0):isbn(book),price(sales_price){} 12 std::string book() const 13 { 14 return isbn; 15 } 16 virtual double net_price(std::size_t n) const 17 { 18 std::cout << "base.net_price " << std::endl; 19 return n*price; 20 } 21 virtual void print() const 22 { 23 std::cout << "base class called..." << std::endl; 24 } 25 virtual ~Item_base() {} 26 27 private: 28 std::string isbn; 29 protected: 30 double price; 31 }; 32 33 34 #endif // !__ITEMBASE__
再看继承类Bulk_item,这里使用了最常见的public继承。文件为Bulk_Item.h。
1 #pragma once 2 #ifndef __BULKITEM__ 3 #define __BULKITEM__ 4 #include "Item_Base.h" 5 #include <iostream> 6 7 8 class Bulk_item:public Item_base 9 { 10 public: 11 Bulk_item(const std::string &book, double sales_price, std::size_t qty = 0, double disc_rate = 0.0) 12 :Item_base(book, sales_price), min_qty(qty), discount(disc_rate) {} 13 14 double net_price(std::size_t n) const 15 { 16 std::cout << "derived.net_price " << std::endl; 17 if (n >= min_qty) 18 { 19 return n*(1 - discount)*price; 20 } 21 else 22 { 23 return n*price; 24 } 25 } 26 27 virtual void print() const 28 { 29 std::cout << "derived class called..." << std::endl; 30 } 31 32 private: 33 std::size_t min_qty; 34 double discount; 35 }; 36 37 38 #endif // !__BULKITEM__
调用main方法的cpp文件为:ClassSample.cpp。
1 // ClassSample.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include "Item_Base.h" 6 #include <iostream> 7 #include "Bulk_Item.h" 8 #include <vector> 9 10 11 using namespace std; 12 13 int main() 14 { 15 Item_base base("1",1); 16 Bulk_item derived("2",2,1,0.5); 17 Item_base &obj1 = derived; 18 Item_base *obj2 = &derived; 19 cout << "begin to test dynamic call.." << endl; 20 obj1.print(); 21 obj2->print(); 22 vector<Item_base> vect; 23 vect.push_back(base); 24 vect.push_back(derived); 25 cout << "begin to loop for object.." << endl; 26 vector<Item_base>::iterator begin = vect.begin(); 27 while (begin != vect.end()) 28 { 29 begin->print(); 30 ++begin; 31 } 32 cout << "begin to loop for pointer.." << endl; 33 34 Item_base *base1 = new Item_base("base",1); 35 Item_base *derived1 = new Bulk_item("derived",2); 36 vector<Item_base* > vect2; 37 vect2.push_back(base1); 38 vect2.push_back(derived1); 39 vector<Item_base* >::iterator begin_pointer = vect2.begin(); 40 while (begin_pointer != vect2.end()) 41 { 42 (*begin_pointer)->print(); 43 ++begin_pointer; 44 } 45 delete base1; 46 delete derived1; 47 system("pause"); 48 return 0; 49 }
在Item_base对象中,有一个print虚函数,这个虚函数在Bulk_item中也有被实现。根据多态的定义,我们使用指针和引用分别调用该虚函数。最终得到的结果是,Bulk_item对象中的print方法被执行。
其次看一下,关于容器和对象。这里使用了vector容器。该容器包含的对象类型是Item_base对象,如果在该容器中存放了Item_base的子类,那么子类会被截断(c++ primer 15.7)。
参考代码begin to loop for object下面的执行结果。
所以我们可以将容器定义为指向Item_base对象的指针,这样就可以存放子类对象。但是,既然是指针,那么需要我们手工来管理这些指针。因为我们要确保,vector对象存在的时候,这些指针所指向的对象也是必须存在的。
参考代码begin to loop for pointer.. 下面的执行结果。
放一张cpp 文件的执行结果:
为了避免我们手工管理这些指针,c++ primer 的15.8 节讲解了句柄对象。下面来看看。
句柄,就是基类指针的包装类,这个包装类自己管理指针。根据个人的学习,我总结了句柄实现的两个功能:
- 句柄必须管理的是指针。
- 句柄必须管理指针的删除。
因为需要通过句柄来实现多态,所以管理的对象必须是指针。所以也必须管理指针的删除。说到底,就是指针的管理。
来看这个句柄的实现:
1 #pragma once 2 #ifndef __SALEITEM__ 3 #define __SALEITEM__ 4 5 #include "Item_Base.h" 6 #include "Bulk_Item.h" 7 8 using namespace std; 9 10 class Sale_item 11 { 12 13 public: 14 Sale_item() :p(0), use(new size_t(1)){} 15 Sale_item(const Item_base &item):p(item.clone()),use(new size_t(1)){} 16 Sale_item(const Sale_item &i) :p(i.p), use(i.use) { ++*use; } 17 ~Sale_item() { decr_use(); } 18 Sale_item& operator=(const Sale_item &rhs) 19 { 20 if (p != rhs.p) 21 { 22 ++*rhs.use; 23 decr_use(); 24 p = rhs.p; 25 use = rhs.use; 26 return *this; 27 } 28 return *this; 29 } 30 Item_base *operator->() 31 { 32 if (p)return p; 33 throw logic_error("unbound Sale_item"); 34 } 35 Item_base &operator*() 36 { 37 if (p)return *p; 38 throw logic_error("unbound Sale_item"); 39 } 40 41 private: 42 Item_base *p; 43 size_t *use; 44 void decr_use() 45 { 46 if (--*use == 0) 47 { 48 delete p; 49 delete use; 50 } 51 } 52 53 }; 54 55 #endif // !__SALEITEM__
句柄既然是包装类,因此我们需要创建一个类,其中有两个成员,一个是父类指针,另外一个是引用计数。因为成员是指针,所以我们需要实现析构函数,根据三法则,我们同时需要自己实现复制控制。
另外,因为句柄必须管理的是指针,因此我们需要其管理的对象必须能够自己返回一个指针,所以在Item_base和Bulk_item对象中都有一个clone函数用来返回该对象的指针。看这两个对象的对应实现,在之前的基础上实现了clone函数。完整代码如下:
Item_base实现如下:
1 #pragma once 2 #ifndef __ITEMBASE__ 3 #define __ITEMBASE__ 4 5 #include <string> 6 #include <iostream> 7 8 class Item_base 9 { 10 public: 11 Item_base(const std::string &book="",double sales_price=0.0):isbn(book),price(sales_price){} 12 std::string book() const 13 { 14 return isbn; 15 } 16 virtual double net_price(std::size_t n) const 17 { 18 std::cout << "base.net_price " << std::endl; 19 return n*price; 20 } 21 virtual void print() const 22 { 23 std::cout << "base class called..." << std::endl; 24 } 25 virtual ~Item_base() {} 26 27 virtual Item_base* clone() const 28 { 29 return new Item_base(*this); 30 } 31 32 private: 33 std::string isbn; 34 protected: 35 double price; 36 }; 37 38 39 #endif // !__ITEMBASE__
Bulk_item实现如下:
1 #pragma once 2 #ifndef __BULKITEM__ 3 #define __BULKITEM__ 4 #include "Item_Base.h" 5 #include <iostream> 6 7 8 class Bulk_item:public Item_base 9 { 10 public: 11 Bulk_item(const std::string &book, double sales_price, std::size_t qty = 0, double disc_rate = 0.0) 12 :Item_base(book, sales_price), min_qty(qty), discount(disc_rate) {} 13 14 double net_price(std::size_t n) const 15 { 16 std::cout << "derived.net_price " << std::endl; 17 if (n >= min_qty) 18 { 19 return n*(1 - discount)*price; 20 } 21 else 22 { 23 return n*price; 24 } 25 } 26 27 virtual void print() const 28 { 29 std::cout << "derived class called..." << std::endl; 30 } 31 32 virtual Bulk_item* clone() const 33 { 34 return new Bulk_item(*this); 35 } 36 37 38 private: 39 std::size_t min_qty; 40 double discount; 41 }; 42 43 44 #endif // !__BULKITEM__
看看cpp测试代码,主要看begin to loop for handler 以下部分:
1 // ClassSample.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include "Item_Base.h" 6 #include <iostream> 7 #include "Bulk_Item.h" 8 #include <vector> 9 #include "Sale_Item.h" 10 11 using namespace std; 12 13 int main() 14 { 15 Item_base base("1",1); 16 Bulk_item derived("2",2,1,0.5); 17 Item_base &obj1 = derived; 18 Item_base *obj2 = &derived; 19 cout << "begin to test dynamic call.." << endl; 20 obj1.print(); 21 obj2->print(); 22 vector<Item_base> vect; 23 vect.push_back(base); 24 vect.push_back(derived); 25 cout << "begin to loop for object.." << endl; 26 vector<Item_base>::iterator begin = vect.begin(); 27 while (begin != vect.end()) 28 { 29 begin->print(); 30 ++begin; 31 } 32 cout << "begin to loop for pointer.." << endl; 33 34 Item_base *base1 = new Item_base("base",1); 35 Item_base *derived1 = new Bulk_item("derived",2); 36 vector<Item_base* > vect2; 37 vect2.push_back(base1); 38 vect2.push_back(derived1); 39 vector<Item_base* >::iterator begin_pointer = vect2.begin(); 40 while (begin_pointer != vect2.end()) 41 { 42 (*begin_pointer)->print(); 43 ++begin_pointer; 44 } 45 delete base1; 46 delete derived1; 47 48 cout << "begin to loop for handler.." << endl; 49 vector<Sale_item> vect3; 50 Item_base base2("base2", 1); 51 Bulk_item derived2("derived2", 2); 52 vect3.push_back(Sale_item(base2)); 53 vect3.push_back(Sale_item(derived2)); 54 55 vector<Sale_item>::iterator begin1 = vect3.begin(); 56 while (begin1 != vect3.end()) 57 { 58 (*begin1)->print(); 59 ++begin1; 60 } 61 62 system("pause"); 63 return 0; 64 }
执行结果如下,vector容器是基于句柄来进行操作的。