(原創) 若class中data member的container,含的是polymorphism的pointer,該如何big three? (C/C++)
Abstract
C++中一旦用到pointer,就得自己管理memory,若功力不夠,不是當機就是memory leak,所以能避免就避免,不過若要在container中放polymorphism object,就只能使用pointer,此時該如何big three呢?
Introduction
在(原創) 若class中的data member有container,而且內含pointer時,也一定要big three!!(C++) 中已經討論了container中放pointer中該如何處理,但別忘了我們為什麼會在container中放pointer呢?就是為了polymorphism,所以一定會遇到一個inheritance hierarchy,而非之前討論的只有一層class。
我們知道處理big three中,主要就是要在copy constructor和assignment operator中重新new過,而非只是copy pointer,但因為container中放的是polymorphism object,我該怎麼針對適當的type去new呢?
我的第一個想法,就是用RTTI去判斷該polymorphism object的type為何,然後去new。
2#include <vector>
3#include <algorithm>
4#include <functional>
5
6using namespace std;
7
8struct DeletePointer {
9 template<typename T>
10 void operator()(const T* ptr) const {
11 if (ptr) {
12 delete ptr;
13 ptr = 0;
14 }
15 }
16};
17
18class Base {
19protected:
20 int _val;
21
22public:
23 Base(int val = 0) : _val(val) {}
24
25 int getValue() {
26 return _val;
27 }
28
29 virtual void print() const {
30 cout << "Base's print:" << _val << endl;
31 }
32};
33
34class Derived1 : public Base {
35public:
36 Derived1(int val = 0) : Base(val) {}
37
38 virtual void print() const {
39 cout << "Derived1's print:" << _val << endl;
40 }
41};
42
43class Derived2 : public Base {
44public:
45 Derived2(int val = 0) : Base(val) {}
46
47 virtual void print() const {
48 cout << "Derived2's print:" << _val << endl;
49 }
50};
51
52class Foo {
53protected:
54 vector<Base*> _bases;
55
56public:
57 Foo() {}
58 Foo(Foo& otherFoo) {
59 for(vector<Base*>::const_iterator iter = otherFoo._bases.begin(); iter != otherFoo._bases.end(); ++iter) {
60 if (dynamic_cast<Derived1*>(*iter))
61 _bases.push_back(new Derived1((*iter)->getValue()));
62 else if (dynamic_cast<Derived2*>(*iter))
63 _bases.push_back(new Derived2((*iter)->getValue()));
64 else
65 _bases.push_back(new Base((*iter)->getValue()));
66 }
67 }
68
69 Foo& operator=(Foo& otherFoo) {
70 for(vector<Base*>::const_iterator iter = otherFoo._bases.begin(); iter != otherFoo._bases.end(); ++iter) {
71 if (dynamic_cast<Derived1*>(*iter))
72 _bases.push_back(new Derived1((*iter)->getValue()));
73 else if (dynamic_cast<Derived2*>(*iter))
74 _bases.push_back(new Derived2((*iter)->getValue()));
75 else
76 _bases.push_back(new Base((*iter)->getValue()));
77 }
78
79 return *this;
80 }
81
82 ~Foo() {
83 for_each(_bases.begin(), _bases.end(), DeletePointer());
84 }
85
86 void insert(Base* base) {
87 _bases.push_back(base);
88 }
89
90 void printElem() const {
91 for_each(_bases.begin(), _bases.end(), mem_fun(&Base::print));
92 }
93};
94
95int main() {
96 Foo foo;
97 foo.insert(new Base(1));
98 foo.insert(new Derived1(2));
99 foo.insert(new Derived2(3));
100
101 // call copy constructor
102 Foo foo2 = foo;
103
104 // call assignment operator
105 Foo foo3;
106 foo3 = foo;
107
108 // call foo's destructor to delete pointer
109 foo.~Foo();
110
111 foo2.printElem();
112 foo3.printElem();
113}
執行結果
Derived1's print:2
Derived2's print:3
Base's print:1
Derived1's print:2
Derived2's print:3
執行結果如預期,但copy contructor和assignment operator非常的醜
for(vector<Base*>::const_iterator iter = otherFoo._bases.begin(); iter != otherFoo._bases.end(); ++iter) {
if (dynamic_cast<Derived1*>(*iter))
_bases.push_back(new Derived1((*iter)->getValue()));
else if (dynamic_cast<Derived2*>(*iter))
_bases.push_back(new Derived2((*iter)->getValue()));
else
_bases.push_back(new Base((*iter)->getValue()));
}
}
由於使用了RTTI,須不斷的去判斷type,這種寫法明顯的違反了OCP,若有新的type,就得改copy constructor和assignment operator的判斷式,算是很差的寫法。
再苦思許久之後,大陸的袁老弟給我一個建議,使用clone(),在Modern C++ Design 8.7節有個範例就是用這種方式,我試了之後,結果非常令我滿意。
Sample Code
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4Filename : ContainerHierarchyWithPointer_BigThree2.cpp
5Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
6Description : Demo how to use big three for pointer in container by clone() in class hierarchy
7Release : 03/16/2007 1.0
8*/
9#include <iostream>
10#include <vector>
11#include <algorithm>
12#include <functional>
13
14using namespace std;
15
16struct DeletePointer {
17 template<typename T>
18 void operator()(const T* ptr) const {
19 if (ptr) {
20 delete ptr;
21 ptr = 0;
22 }
23 }
24};
25
26class Base {
27protected:
28 int _val;
29
30public:
31 Base(int val = 0) : _val(val) {}
32
33 int getValue() {
34 return _val;
35 }
36
37 virtual Base* clone() const {
38 return new Base(*this);
39 }
40
41 virtual void print() const {
42 cout << "Base's print:" << _val << endl;
43 }
44};
45
46class Derived1 : public Base {
47public:
48 Derived1(int val = 0) : Base(val) {}
49
50 virtual Derived1* clone() const {
51 return new Derived1(*this);
52 }
53
54 virtual void print() const {
55 cout << "Derived1's print:" << _val << endl;
56 }
57};
58
59class Derived2 : public Base {
60public:
61 Derived2(int val = 0) : Base(val) {}
62
63 virtual Derived2* clone() const {
64 return new Derived2(*this);
65 }
66
67 virtual void print() const {
68 cout << "Derived2's print:" << _val << endl;
69 }
70};
71
72class Foo {
73protected:
74 vector<Base*> _bases;
75
76public:
77 Foo() {}
78
79 Foo(Foo& otherFoo) {
80 transform(otherFoo._bases.begin(), otherFoo._bases.end(), back_inserter(_bases), mem_fun(&Base::clone));
81 }
82
83 Foo& operator=(Foo& otherFoo) {
84 transform(otherFoo._bases.begin(), otherFoo._bases.end(), back_inserter(_bases), mem_fun(&Base::clone));
85
86 return *this;
87 }
88
89 ~Foo() {
90 for_each(_bases.begin(), _bases.end(), DeletePointer());
91 }
92
93 void insert(Base* base) {
94 _bases.push_back(base);
95 }
96
97 void printElem() const {
98 for_each(_bases.begin(), _bases.end(), mem_fun(&Base::print));
99 }
100};
101
102int main() {
103 Foo foo;
104 foo.insert(new Base(1));
105 foo.insert(new Derived1(2));
106 foo.insert(new Derived2(3));
107
108 // call copy constructor
109 Foo foo2 = foo;
110
111 // call assignment operator
112 Foo foo3;
113 foo3 = foo;
114
115 // call foo's destructor to delete pointer
116 foo.~Foo();
117
118 foo2.printElem();
119 foo3.printElem();
120}
執行結果
Derived1's print:2
Derived2's print:3
Base's print:1
Derived1's print:2
Derived2's print:3
37行
return new Base(*this);
}
在base class多定義了clone member function,專門負責做clone。
50行
return new Derived1(*this);
}
重點來了,derived class override了clone(),但return type變成Derived1*,而不是Base*,這和我們一般polymorphism寫法不一樣,Modern C++ Design 8.7節稱此為covariant return type,讓你override virtual時,可以不使用Base *,利用這個特色,我們就能clone出完整的object。
79行
transform(otherFoo._bases.begin(), otherFoo._bases.end(), back_inserter(_bases), mem_fun(&Base::clone));
}
copy constructor就變的非常的簡單,而且mem_fun(&Base::clone)還支援polymorphism,相當漂亮,程式只要一行就解決,而且符合OCP,若未來有增加type,也不用再修改程式。
Conclusion
Modern C++ Design 8.7節指出這種使用clone的方式有兩個缺點:
1.必須在base class增加virtual clone(),若使用的是component而無法修改程式時,將無法使用這種方法。
2.每個derived class一定要去override base class的virtual clone(),若忘記override clone(),程式結果將有錯誤,而且compiler也不會提醒你。
Modern C++ Design因此提出了Clone Factory Pattern來解決這個問題,不過我還沒看懂這個pattern,但目前看起來使用clone()的方式還算不錯。
See Also
(原創) 若class中的data member有container,而且內含pointer時,也一定要big three!!(C/C++)
Reference
Andrei Alexandrescu,Modern C++ Design,Addison Weseley,2001