(原創) 若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。
#include <iostream>2
#include <vector>3
#include <algorithm>4
#include <functional>5

6
using namespace std;7

8
struct 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

18
class Base {19
protected:20
int _val;21
22
public: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

34
class Derived1 : public Base {35
public:36
Derived1(int val = 0) : Base(val) {}37
38
virtual void print() const {39
cout << "Derived1's print:" << _val << endl;40
}41
};42

43
class Derived2 : public Base {44
public:45
Derived2(int val = 0) : Base(val) {}46
47
virtual void print() const {48
cout << "Derived2's print:" << _val << endl;49
}50
};51

52
class Foo {53
protected:54
vector<Base*> _bases;55
56
public: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

95
int 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 constructor102
Foo foo2 = foo;103
104
// call assignment operator105
Foo foo3;106
foo3 = foo;107
108
// call foo's destructor to delete pointer109
foo.~Foo();110
111
foo2.printElem();112
foo3.printElem();113
}
執行結果
Base's print:1
Derived1's print:2
Derived2's print:3
Base's print:1
Derived1's print:2
Derived2's print:3
執行結果如預期,但copy contructor和assignment operator非常的醜
Foo(Foo& otherFoo) {
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.com3

4
Filename : ContainerHierarchyWithPointer_BigThree2.cpp5
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++6
Description : Demo how to use big three for pointer in container by clone() in class hierarchy7
Release : 03/16/2007 1.08
*/9
#include <iostream>10
#include <vector>11
#include <algorithm>12
#include <functional>13

14
using namespace std;15

16
struct 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

26
class Base {27
protected:28
int _val;29
30
public: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

46
class Derived1 : public Base {47
public: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

59
class Derived2 : public Base {60
public: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

72
class Foo {73
protected:74
vector<Base*> _bases;75
76
public: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

102
int 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 constructor109
Foo foo2 = foo;110
111
// call assignment operator112
Foo foo3;113
foo3 = foo;114
115
// call foo's destructor to delete pointer116
foo.~Foo();117
118
foo2.printElem();119
foo3.printElem();120
}
執行結果
Base's print:1
Derived1's print:2
Derived2's print:3
Base's print:1
Derived1's print:2
Derived2's print:3
37行
virtual Base* clone() const {
return new Base(*this);
}
在base class多定義了clone member function,專門負責做clone。
50行
virtual Derived1* clone() const {
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行
Foo(Foo& otherFoo) {
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


浙公网安备 33010602011771号