Abstract
當class的data member含pointer時,我們知道此時一定要big three(copy constructor,assignment operator,destructor),若是container內含pointer時呢?答案是也需big three。
Introduction
首先做個實驗,有一個vector為v1,內含Foo*,我們希望clone出一個vector v2,使用vector所提供的copy constructor進行clone。
1
#include
<
iostream
>
2
#include
<
vector
>
3

4
using
namespace
std;
5

6

class
Foo
{
7
private:
8
int _value;
9
10
public:
11
Foo(int n = 0) : _value(n)
{}
12
13
int getValue()
{
14
return _value;
15
}
16
}
;
17

18

int
main()
{
19
vector<Foo*> v1;
20
v1.push_back(new Foo(1));
21
v1.push_back(new Foo(2));
22
v1.push_back(new Foo(3));
23
24
cout << "vector v1:" << endl;
25
for(vector<Foo*>::iterator iter = v1.begin(); iter != v1.end(); ++iter)
26
cout << (*iter)->getValue() << " ";
27
28
vector<Foo*>v2(v1);
29
30
cout << endl << "vector v2:" << endl;
31
for(vector<Foo*>::iterator iter = v2.begin(); iter != v2.end(); ++iter)
32
cout << (*iter)->getValue() << " ";
33
34
cout << endl;
35
}
執行結果
vector v1:
1
2
3
vector v2:
1
2
3
28行
vector
<
Foo
*>
v2(v1);
使用vector的copy constructor進行clone。
現在我們將v1內的pointer刪除,若v2能正常顯示,則表示v2 clone成功。
1
#include
<
iostream
>
2
#include
<
vector
>
3

4
using
namespace
std;
5

6

class
Foo
{
7
private:
8
int _value;
9
10
public:
11
Foo(int n = 0) : _value(n)
{}
12
13
int getValue()
{
14
return _value;
15
}
16
}
;
17

18

int
main()
{
19
vector<Foo*> v1;
20
v1.push_back(new Foo(1));
21
v1.push_back(new Foo(2));
22
v1.push_back(new Foo(3));
23
24
cout << "vector v1:" << endl;
25
for(vector<Foo*>::iterator iter = v1.begin(); iter != v1.end(); ++iter)
26
cout << (*iter)->getValue() << " ";
27
28
vector<Foo*>v2(v1);
29
30
for(vector<Foo*>::iterator iter = v1.begin(); iter != v1.end(); ++iter)
31
delete *iter;
32
33
cout << endl << "vector v2:" << endl;
34
for(vector<Foo*>::iterator iter = v2.begin(); iter != v2.end(); ++iter)
35
cout << (*iter)->getValue() << " ";
36
37
cout << endl;
38
}
執行結果
vector v1:
1
2
3
vector v2:
-
572662307
-
572662307
-
572662307
30~31行
for
(vector
<
Foo
*>
::iterator iter
=
v1.begin(); iter
!=
v1.end();
++
iter)
delete
*
iter;
只要將v1中的pointer delete,v2就無法顯示,證明vector的copy constructor並非真正的clone,只是將pointer copy過去,這樣會造成v1和v2中的pointer指的是同一個object,只要v1一delete的,v2也無法正常了。
正確的寫法如下
1
/**/
/*
2
(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4
Filename : ContainerWithPointer_Clone.cpp
5
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
6
Description : Demo how to clone container with pointer
7
Release : 05/18/2007 1.0
8
*/
9
#include
<
iostream
>
10
#include
<
vector
>
11
#include
<
algorithm
>
12
#include
<
functional
>
13

14
using
namespace
std;
15

16

struct
printElem
{
17
template <typename T>
18
void operator()(T*& iter) const
{
19
cout << iter->getValue() << " ";
20
}
21
}
;
22

23

class
Foo
{
24
private:
25
int _value;
26
27
public:
28
Foo(int n = 0) : _value(n)
{}
29
30
int getValue()
{
31
return _value;
32
}
33
}
;
34

35

int
main()
{
36
vector<Foo*> v1;
37
v1.push_back(new Foo(1));
38
v1.push_back(new Foo(2));
39
v1.push_back(new Foo(3));
40
41
cout << "vector v1:" << endl;
42
for_each(v1.begin(), v1.end(), printElem());
43
44
vector<Foo*> v2;
45
for(vector<Foo*>::iterator iter = v1.begin(); iter != v1.end(); ++iter)
{
46
v2.push_back(new Foo((*iter)->getValue()));
47
}
48
49
for(vector<Foo*>::iterator iter = v1.begin(); iter != v1.end(); ++iter)
50
delete *iter;
51
52
cout << endl << "vector v2:" << endl;
53
for_each(v2.begin(), v2.end(), printElem());
54
55
cout << endl;
56
}
執行結果
vector v1:
1
2
3
vector v2:
1
2
3
44行
vector
<
Foo
*>
v2;

for
(vector
<
Foo
*>
::iterator iter
=
v1.begin(); iter
!=
v1.end();
++
iter)
{
v2.push_back(new Foo((*iter)->getValue()));
}
唯有重新new過,這樣才叫做真正的clone。
以下介紹兩種方法,達成big three的要求。
1.方法一:使用一般big three的方式。
1
/**/
/*
2
(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4
Filename : ContainerWithPointer_BigThree.cpp
5
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
7
Release : 05/18/2007 1.0
8
*/
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
Foo
{
27
private:
28
int _value;
29
30
public:
31
Foo(int n = 0) : _value(n)
{}
32
33
int getValue() const
{
34
return _value;
35
}
36
}
;
37

38

class
Moo
{
39
private:
40
vector<Foo*> _foos;
41
42
public:
43
Moo()
{}
44
// copy constructor
45
Moo(Moo& otherMoo)
{
46
for(vector<Foo*>::iterator iter = otherMoo._foos.begin(); iter != otherMoo._foos.end(); ++iter)
{
47
_foos.push_back(new Foo((*iter)->getValue()));
48
}
49
}
50
51
// Assignment operator
52
Moo& operator=(Moo& otherMoo)
{
53
for(vector<Foo*>::iterator iter = otherMoo._foos.begin(); iter != otherMoo._foos.end(); ++iter)
{
54
_foos.push_back(new Foo((*iter)->getValue()));
55
}
56
57
return *this;
58
}
59
60
// Destructor
61
~Moo()
{
62
for_each(_foos.begin(), _foos.end(),DeletePointer());
63
}
64
65
void insert(Foo* foo)
{
66
_foos.push_back(foo);
67
}
68
69
void printElem() const
{
70
transform(_foos.begin(), _foos.end(), ostream_iterator<int>(cout, " "), mem_fun(&Foo::getValue));
71
72
cout << endl;
73
}
74
}
;
75

76

int
main()
{
77
Moo moo1;
78
moo1.insert(new Foo(1));
79
moo1.insert(new Foo(2));
80
moo1.insert(new Foo(3));
81
cout << "moo1:";
82
moo1.printElem();
83
84
// use copy constructor
85
Moo moo2(moo1);
86
Moo moo3 = moo1;
87
88
// use assign operator
89
Moo moo4;
90
moo4 = moo1;
91
92
// call moo1's destuctor to delete all pointer
93
moo1.~Moo();
94
95
cout << "moo2:";
96
moo2.printElem();
97
cout << "moo3:";
98
moo3.printElem();
99
cout << "moo4:";
100
moo4.printElem();
101
}
執行結果
moo1:
1
2
3
moo2:
1
2
3
moo3:
1
2
3
moo4:
1
2
3
16行
struct
DeletePointer
{
template<typename T>

void operator()(const T* ptr) const
{

if (ptr)
{
delete ptr;
ptr = 0;
}
}
}
;
是effective STL item 7所介紹刪除pointer的方法,花了二頁的篇幅介紹為什麼不用class template而用function object的function template。
44行
//
copy constructor

Moo(Moo
&
otherMoo)
{

for(vector<Foo*>::iterator iter = otherMoo._foos.begin(); iter != otherMoo._foos.end(); ++iter)
{
_foos.push_back(new Foo((*iter)->getValue()));
}
}
重新改寫copy constructor,避免compiler只是做pointer的copy,而非值的copy。
51行
//
Assignment operator

Moo
&
operator
=
(Moo
&
otherMoo)
{

for(vector<Foo*>::iterator iter = otherMoo._foos.begin(); iter != otherMoo._foos.end(); ++iter)
{
_foos.push_back(new Foo((*iter)->getValue()));
}
return *this;
}
重新改寫assign operator,避免compiler只是做pointer的assign,而非值的assign。
62行
for_each(_foos.begin(), _foos.end(),DeletePointer());
手動刪除所有pointer。
93行
//
call moo1's destuctor to delete all pointer
moo1.
~
Moo();
呼叫了moo1的destructor,故意刪除了moo1中所有的pointer,以測試copy constructor和assignment operator是否正常。
方法二:另外加上clone() member function。
1
/**/
/*
2
(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4
Filename : ContainerWithPointer_BigThree2.cpp
5
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()
7
Release : 05/18/2007 1.0
8
*/
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
Foo
{
27
private:
28
int _value;
29
30
public:
31
Foo(int n = 0) : _value(n)
{}
32
33
virtual Foo* clone() const
{
34
return new Foo(*this);
35
}
36
37
int getValue() const
{
38
return _value;
39
}
40
}
;
41

42

class
Moo
{
43
private:
44
vector<Foo*> _foos;
45
46
public:
47
Moo()
{}
48
// copy constructor
49
Moo(Moo& otherMoo)
{
50
transform(otherMoo._foos.begin(), otherMoo._foos.end(), back_inserter(_foos), mem_fun(&Foo::clone));
51
}
52
53
// Assignment operator
54
Moo& operator=(Moo& otherMoo)
{
55
transform(otherMoo._foos.begin(), otherMoo._foos.end(), back_inserter(_foos), mem_fun(&Foo::clone));
56
57
return *this;
58
}
59
60
// Destructor
61
~Moo()
{
62
for_each(_foos.begin(), _foos.end(),DeletePointer());
63
}
64
65
void insert(Foo* foo)
{
66
_foos.push_back(foo);
67
}
68
69
void printElem() const
{
70
transform(_foos.begin(), _foos.end(), ostream_iterator<int>(cout, " "), mem_fun(&Foo::getValue));
71
72
cout << endl;
73
}
74
}
;
75

76

int
main()
{
77
Moo moo1;
78
moo1.insert(new Foo(1));
79
moo1.insert(new Foo(2));
80
moo1.insert(new Foo(3));
81
cout << "moo1:";
82
moo1.printElem();
83
84
// use copy constructor
85
Moo moo2(moo1);
86
Moo moo3 = moo1;
87
88
// use assign operator
89
Moo moo4;
90
moo4 = moo1;
91
92
// call moo1's destuctor to delete all pointer
93
moo1.~Moo();
94
95
cout << "moo2:";
96
moo2.printElem();
97
cout << "moo3:";
98
moo3.printElem();
99
cout << "moo4:";
100
moo4.printElem();
101
}
執行結果
moo1:
1
2
3
moo2:
1
2
3
moo3:
1
2
3
moo4:
1
2
3
33行
virtual
Foo
*
clone()
const
{
return new Foo(*this);
}
自己加上一個clone() member function。
48行
Moo(Moo
&
otherMoo)
{
transform(otherMoo._foos.begin(), otherMoo._foos.end(), back_inserter(_foos), mem_fun(&Foo::clone));
}
因為已經有了clone(),copy constructor變的非常精簡,只需用transform()複製vector即可,注意此處不能用copy(),因為copy()只能針對vector內的iterator做copy,而不能呼叫iterator的member function,所以在此可以發現trnasform()比copy()功能強大。
雖然使用clone()看似方便,程式非常精簡,但clone()有個缺點,就是得另外增加clone(),若使用component無法修改source code時,則無法達成。
若在inheritance hierarchy下,使用方法一將非常麻煩,方法二可獲得相當漂亮的解法。
Conclusion
當container內裝pointer時,也必須乖乖的big three,才能保證執行結果正確,我承認這是C++很麻煩的地方,但C++的style就是如此,class內不用pointer則已,一旦用了pointer就得big three。
See Also
(原創) 若class中data member的container,含的是polymorphism的pointer,該如何big three? (高級) (C++) (OO C++)