类中指针数据成员的处理

上一篇 / 下一篇  2012-10-16 16:00:00 / 个人分类:杂谈

51Testing软件测试网UGRO9I]U

  一般而言,类中只含有内置类型时,只使用编译器提供的默认constructor函数、默认destructor和默认overloaded assignment operator(重载操作符)即可,但是一旦有了指针数据成员,具体的说是指向堆中的值的指针数据成员,就得另当别论了。51Testing软件测试网ql%HhN

!\CA0i:do Vm0  由于编译器添加的默认函数都比较简单,对于比较简单的类而言,通常没有什么问题,但是当类中有数据成员指向堆中的值时,什么都需要程序员自己做了。

j&`r1D'~1n8Be0z0

C2pA R^0  “实践出真知”,现在拿出一个比较好的代码分析:51Testing软件测试网GM!r dZ6G3`3J

51Testing软件测试网F"~6CkP"J!?p2I

  这是一个简单的animal的类,只有两个数据成员,但其中又一个是string*类型的,它是我们讨论的对象。51Testing软件测试网p0WTo$f4aA

51Testing软件测试网JV;w lZm$B3K

//Heap Data Member51Testing软件测试网#U kT;D6qp*~
//Demonstrates an object with a dynamically allocated data member

q4o(C[Dd ~,LF,K*j4Z051Testing软件测试网r.I3_G QXA

51Testing软件测试网Hq4X:t$ne\x
#include <iostream>
%EQ['Ti6NVJJ5w2y6e0#include <string>

"}(X6J{Q0

[m^5q {8F r051Testing软件测试网R w4`%z2Y1mzR+y
using namespace std;
51Testing软件测试网uPj%[3Y

4T Kb'T9N/Ax051Testing软件测试网#y0F V'g-l
class Animal
]u6LQ;Y%]3H J7R0{
`Pp]G0?0public:&nbsp;
4QNGV@2|6|UV!R0&nbsp; &nbsp; Animal(const string& name = "", int age = 0); &nbsp;
4z4OC(UJ%c#n0&nbsp; &nbsp; ~Animal(); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<SPAN style="WHITE-SPACE: pre">    </SPAN> <SPAN style="WHITE-SPACE: pre"> </SPAN>//destructor prototype &nbsp;&nbsp;
oI [%\1T0&nbsp; &nbsp; Animal(const Animal& c); &nbsp; &nbsp;<SPAN style="WHITE-SPACE: pre">     </SPAN>//copy constructor prototype51Testing软件测试网v-Ui&]G-{{1Zc
&nbsp; &nbsp; Animal& Animal::operator=(const Animal& c); &nbsp;<SPAN style="WHITE-SPACE: pre"> </SPAN>//assignment operator&nbsp;
_Y,tBc0&nbsp; &nbsp; void Greet() const;&nbsp;
51Testing软件测试网:~q6?H.B

#Rz YHCs+f c{o051Testing软件测试网v(Ak:R_)p#V$S$dj)c
private:51Testing软件测试网;Q&|6Y\6xb+@$v
&nbsp; &nbsp; string* m_pName;<SPAN style="WHITE-SPACE: pre"> </SPAN>//★要注意的关键&nbsp;
T ] [ m%Wn l:i6c8bg0&nbsp; &nbsp; int m_Age;
2sX"to8U7h1k sxW7o0};

;Zu1xyB9V8Z051Testing软件测试网W~a-kQ;[

51Testing软件测试网yF)y3?ucc7E
Animal::Animal(const string& name, int age)51Testing软件测试网]"UpV:I$|,i
{51Testing软件测试网%B-x$Ii&S-N3@ Z A
&nbsp; &nbsp; cout << "(构造函数被调用)\n";51Testing软件测试网.I%j`V'k)P3c{
&nbsp; &nbsp; m_pName = new string(name);<SPAN style="WHITE-SPACE: pre">  </SPAN>//另外分配内存空间&nbsp;51Testing软件测试网-@)F&P)b"m$H
&nbsp; &nbsp; m_Age = age; &nbsp;
6f D]sw2j6ZB0}

)jfs{S|w.D0

w4|x1^Nx0
b6];aR}pY0Animal::~Animal() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //手工定义析构函数的必要性&nbsp;
g hk"a(qs+NM+m0{
;g,j ]$WGP2z"}d0{0&nbsp; &nbsp; cout << "(析构函数被调用)\n";
^:W#j.E[6n]Y6Sz a0&nbsp; &nbsp; delete m_pName;<SPAN style="WHITE-SPACE: pre">      </SPAN>//释放内存空间&nbsp;
[.L S*\#]8?0}
51Testing软件测试网*H X[;d [$~8Z"c;bed9t4t)P

51Testing软件测试网~)v$x1H _-x


lX_V\B2SI0Animal::Animal(const Animal& c) &nbsp; &nbsp; &nbsp; &nbsp;<SPAN style="WHITE-SPACE: pre"> </SPAN>//手工定义拷贝构造函数的必要性&nbsp;51Testing软件测试网LSM4o0?_e| p
{
!G1Q3_gQ2eV!D0e$O2Y K0&nbsp; &nbsp; cout << "(拷贝构造函数被调用)\n";
+VE9NA]:]|+?0&nbsp; &nbsp; m_pName = new string(*(c.m_pName));<SPAN style="WHITE-SPACE: pre"> </SPAN>//实现“深拷贝”的关键&nbsp;
/D7W#f(|:Q0&nbsp; &nbsp; m_Age = c.m_Age;
Lu{B+h1cc,W0}
51Testing软件测试网O?ay wb

51Testing软件测试网o$ct9tj8Mw


k&H2`{SI u;f^0Animal& Animal::operator=(const Animal& c) &nbsp;//手工定义操作符重载的必要性&nbsp;
0r/C#?8QR.f0{
C b|^2c:Q0&nbsp; &nbsp; cout << "(重载操作符被调用)\n";
7Be,k'z8@\6~%i*o0&nbsp; &nbsp; if (this != &c)
FO \4|'vf![1u0&nbsp; &nbsp; {51Testing软件测试网S^IKxY X
&nbsp; &nbsp; &nbsp; &nbsp; delete m_pName;//别忘了释放掉原先的内存51Testing软件测试网|w5x"[$n
&nbsp; &nbsp; &nbsp; &nbsp; m_pName = new string(*(c.m_pName));51Testing软件测试网Bb\+w` M
&nbsp; &nbsp; &nbsp; &nbsp; m_Age = c.m_Age;
4YM a;e nJ%X0&nbsp; &nbsp; }51Testing软件测试网 L s[ vd!WeN
&nbsp; &nbsp; return *this;51Testing软件测试网8x1Ccc s7y S
}

g/lL+f3t{V;J)S*S051Testing软件测试网z%yW3|pS4fM&B(B sf


7FX3zJ@0void Animal::Greet() const
O&j9U/E Q0Bph0{
Am{.p&ysmS0&nbsp; &nbsp; &nbsp;cout << "你好,我叫" << *m_pName << " ,我今年" << m_Age << "岁了。 ";
b?0j4@+JQ0&nbsp; &nbsp; &nbsp;cout << "&m_pName: " << cout << &m_pName << endl;51Testing软件测试网 Xg"n^(D
}
51Testing软件测试网.UZ4~&I t"J]c

Xr9eS0b!iMP0
P"X l%xa$X0//声明3个测试函数&nbsp;
Z b|Ms0void testDestructor();
_tN,I/D%b$]0void testCopyConstructor(Animal aCopy);51Testing软件测试网9_i(j3R&C_z
void testAssignmentOp();

!eJg9?t0

@AU-o Ng0
vO jb(c {T0int main()51Testing软件测试网9B`9F5y?lC(g5W
{51Testing软件测试网RL#_$R)P?
&nbsp; &nbsp; testDestructor();51Testing软件测试网 ],I8Tcw6y8FK'm
&nbsp; &nbsp; cout << endl;
p+Hl,L U6DB0&nbsp; &nbsp;&nbsp;
lX;Lm3R%y0&nbsp; &nbsp; Animal crit("Poochie", 5);
]V ]'a1@F^P't0&nbsp; &nbsp; crit.Greet();51Testing软件测试网QiFVxk1bt6P,~%vj7o
&nbsp; &nbsp; testCopyConstructor(crit);51Testing软件测试网/zV7AI/S3vf
&nbsp; &nbsp; crit.Greet();
%Bo6~t8?(x4Kb(c:B0&nbsp; &nbsp; cout << endl;51Testing软件测试网S_J%hd+M8t6m S
&nbsp; &nbsp;&nbsp;
_5E0[T Z&c(|0&nbsp; &nbsp; testAssignmentOp();
51Testing软件测试网s^ R TE

51Testing软件测试网cT:B+R"W'N~2Dv0W-J


Smm!qF)C:p0&nbsp; &nbsp; return 0;
@Wn ^X M3~ P D0}
51Testing软件测试网9m]8Ml$y(S

`!_z H;o!`zj0
$w1k5h?7Tz4M0void testDestructor()51Testing软件测试网9jT3l/Rd
{
%cq_ MQ&r]xi0&nbsp; &nbsp; Animal toDestroy("Rover", 3);
'g3c7oP0e4k^"n'T `q0&nbsp; &nbsp; toDestroy.Greet();
y[/xH!enT f0}<SPAN style="WHITE-SPACE: pre"> </SPAN>//运行结束时,toDestroy对象随即被销毁&nbsp;

D2u xX!d NZiE0

+k}q#\^mI051Testing软件测试网XUr mi7jG
void testCopyConstructor(Animal aCopy) &nbsp;//注:不是引用类型&nbsp;
~mI DF,QO `vZ0{
,w"i}mw/l(d1n#tZ0&nbsp; &nbsp; aCopy.Greet();51Testing软件测试网hT+v Ms&c wj[
}
51Testing软件测试网2O6sj\~L!~0|Yt@

51Testing软件测试网}!^4dw5W(a


Gx'T-d u;rcH;W @^0void testAssignmentOp()51Testing软件测试网|.L7uoW5l#P"Bc
{
"P3Vh3QU0&nbsp; &nbsp; Animal ani1("ani1", 7);51Testing软件测试网 D XC,P_WT-_%Oe
&nbsp; &nbsp; Animal ani2("ani2", 9);51Testing软件测试网:j GP(E] Ev
&nbsp; &nbsp; ani1 = ani2;
:Y(~L Z WN~0&nbsp; &nbsp; ani1.Greet(); &nbsp;
P8E(HsA.b0U8SG0&nbsp; &nbsp; ani2.Greet();51Testing软件测试网I5xA \$_+\5n(q
&nbsp; &nbsp; cout << endl;51Testing软件测试网X B6^&u;J1]`2MmE
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;51Testing软件测试网-d H+h3C kAH7KZ#bv
&nbsp; &nbsp; Animal ani3("ani", 11);
@_W}bZ0&nbsp; &nbsp; ani3 = ani3;51Testing软件测试网2n8[+y!f)x s4J c
&nbsp; &nbsp; ani3.Greet();
yPLeys0}

:H CA&tgP a8n051Testing软件测试网#~!Ti)mEc{

  运行结果:51Testing软件测试网a~^&p/hwL

@^nX~7L/Z-t0

  可以发现:Animal对象被销毁、复制以及相互赋值是,对这些数据成员做出了不同的处理,具体分析如下。51Testing软件测试网%XJb MdQ!w&T'K

  析构函数

s0Q%f#sz"\)X:ZD0

  当对象的数据成员指向堆中的值时,可能产生的问题就是内存泄露。如果不编写自己的析构函数,则编译器会替程序员创建 一个默认析构函数,但她并不尝试释放掉任何数据成员指向的堆中的内存。当类中有数据成员指向堆中值时,则应当编写自己的析构函数,以便能在对象消失以前释 放掉与对象相关的堆中内存,避免内存泄露。所以在这里的析构函数必须对申请的堆中内存做相应处理:51Testing软件测试网 vm^-_w J3I5i0~\

51Testing软件测试网#z Wan8[7Mj

    delete m_pName;      //释放内存空间

  main函数中调用testDestructor()时测试了析构函数。它创建了一个toDestroy的对象,并 且打印出m_pName中的堆中字符的地址。当testDestructor()调用完毕要返回main时,自动调用了析构函数,释放掉 toDestroy对象占用的内存,包括“Rover”字符串占用的堆中内存。析构函数对m_Age没有做任何处理,这完全没有问题,因为m_Age不在 堆中,而是toDestroy的一部分,并且会随Animal对象的其余部分被妥善的处理。51Testing软件测试网6qF;k|*B!D3k

  总之:如果在堆中分配内存,则应当编写析构函数来清理与释放堆中的内存。

+g6ol2d#Um:L$Aw0

  拷贝构造函数51Testing软件测试网+lKZ4s/Z$Gb

  同构造函数和析构函数一样简单,编译前生成的默认拷贝构造函数只是简单地将每个数据成员的值复制给新对象同名数据成 员,即按成员逐项进行复制。但在我们这个函数中却不能在使用默认拷贝构造函数,原因还是m_pName的存在。如果只是用默认拷贝构造函数,对象的自动复 制将会导致新的对象指向堆中的同一个字符串,因为新对象的指针仅仅获得存储在原始对象的指针中地址的一个副本。这就造成了数据的浅拷贝。真正需要的拷贝构 造函数是能让新生成的对象在堆中拥有自己的内存块,对象中的每个数据成员都指向一个队中的对象,这就是深拷贝。所以在Animal类的默认拷贝构造函数需 要对m_pName成员分配新的堆内存。51Testing软件测试网a$m&KzZ*zF_ Y

kF-^^5q*dl-yFk j0
    m_pName = new string(*(c.m_pName)); 

  观察程序,发现当testCopyConstructor()时使用的m_pName堆中地址与main中crit 使用的m_pName地址并不相同,这就实现了堆内存数据的复制。当testCopyConstructor()执行结束时,析构函数被调用,释放了对象 内存。51Testing软件测试网9V:@BR%| |E

  总之:当类的数据成员指向堆中内存时,应当考虑编写手动拷贝构造函数来为新对象分配内存,实现深拷贝。51Testing软件测试网%N@-qJ+mj*zRW_

  赋值运算符的重载

"r*f3A;K(X4i:w0

  同上述几个函数一样,如果程序员没有编写自己的赋值运算符成员函数,编译器就会为程序员提供一个默认的成员函数,但这也是相当简单的。51Testing软件测试网1VAd,|l"]2n D6a

  如果只是用默认的赋值运算符成员函数,也只是实现的浅拷贝。所以Animal的赋值运算符成员函数应当写成:51Testing软件测试网5b[&u*u}#@

q"MyOOV0
Animal& Animal::operator=(const Animal& c)  //手工定义操作符重载的必要性51Testing软件测试网ags0C"WE
{
m7k'M,|}qO7x0    cout << "(重载操作符被调用)\n";
J5P&asd$l0    if (this != &c)
c9]$No%hZ M G"W0    {51Testing软件测试网y8F[}f#aN
        delete m_pName;//别忘了释放掉原先的内存
m w Z!qNy \"C nT0        m_pName = new string(*(c.m_pName));
^(VFYW8lc0        m_Age = c.m_Age;51Testing软件测试网+}4v.E4Ho6X WWQ
    }51Testing软件测试网"|2HX6Q2r%z-e
    return *this;
i2J'LKL?1e4qt0}

  main中的testAssignmentOp()测试了赋值运算符的重载。

cIgDrc0

  总之:当类中有数据成员指向堆中内存时,应当考虑为该类重载赋值运算符。51Testing软件测试网 j)xh^^!H

  本文出处:http://write.blog.csdn.net/postedit/804637951Testing软件测试网 } N,Wif3{`z"M


TAG:

 

评分:0

我来说两句

Open Toolbar