在<<c++ primer>>第四版的第15章中详细讲解了类的定义,继承关系,多态。这里主要实现这一章中的例子。
首先看定义Item_base对象,文件为Item_Base.h。
1 #pragma onceView Code
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 onceView Code
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 : 定义控制台应用程序的入口点。View Code
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 onceView Code
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 onceView Code
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 onceView Code
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 : 定义控制台应用程序的入口点。View Code
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容器是基于句柄来进行操作的。