类中指针数据成员的处理

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

51Testing软件测试网-fe&|&rGe{1G {6kp

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

51Testing软件测试网&BB_o{^:g

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

8h(UO2X-q [0

.]r'M-@#{H0  “实践出真知”,现在拿出一个比较好的代码分析:51Testing软件测试网#|;h3gM&k]/{(H

51Testing软件测试网lJi3D!Eq2f

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

tB,j^"zm,k0//Heap Data Member51Testing软件测试网7|dm7H6nV
//Demonstrates an object with a dynamically allocated data member

@_s)b:Q)v yAO051Testing软件测试网ro`/JU E%t


Q9@1od!|"y ZP0#include <iostream>
s&bdncM0#include <string>

(J-TZ*JM'{0

8wi8~E `of!^k;F0
ci1GK$f;`)j0using namespace std;

vU.SJ&X P&O Y:q)I051Testing软件测试网K)l$I F}@8U

51Testing软件测试网(v n1BOM,fq d Cd
class Animal51Testing软件测试网 g2dPQ6W#j;C a3?
{
o I,Oo,bsU|R5J?0public:&nbsp;
]P3^kB3~3o9D8Y [0&nbsp; &nbsp; Animal(const string& name = "", int age = 0); &nbsp;
F4^/ZLi&E"F] E0&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;
4jY-L] @W1R%iq0&nbsp; &nbsp; Animal(const Animal& c); &nbsp; &nbsp;<SPAN style="WHITE-SPACE: pre">     </SPAN>//copy constructor prototype51Testing软件测试网E1BAd C"B TN(D
&nbsp; &nbsp; Animal& Animal::operator=(const Animal& c); &nbsp;<SPAN style="WHITE-SPACE: pre"> </SPAN>//assignment operator&nbsp;51Testing软件测试网{ x]ho`"j#w#{;N
&nbsp; &nbsp; void Greet() const;&nbsp;
51Testing软件测试网y V \6Ly6y+d?

51Testing软件测试网RC"o"@&QF3J

51Testing软件测试网!]?.CAG
private:
nGMN8?Y0OI0&nbsp; &nbsp; string* m_pName;<SPAN style="WHITE-SPACE: pre"> </SPAN>//★要注意的关键&nbsp;51Testing软件测试网 Z5kQ0pXtg TG*T
&nbsp; &nbsp; int m_Age;51Testing软件测试网%l)N7g6k-L!b3E*U
};

'[u!T(a?&C%x*F051Testing软件测试网H:|Xh`$@D,y


4E!c0d{I3{'I0Animal::Animal(const string& name, int age)
0yQ y1Zb t,{0{
,[ d9x@|7k1DP0&nbsp; &nbsp; cout << "(构造函数被调用)\n";
3xlH an4OF!T wQ0&nbsp; &nbsp; m_pName = new string(name);<SPAN style="WHITE-SPACE: pre">  </SPAN>//另外分配内存空间&nbsp;
&G&|'S&K |6r ||it3{0&nbsp; &nbsp; m_Age = age; &nbsp;51Testing软件测试网#z YL9L;}
}
51Testing软件测试网N'd_#^0UK(Q#YX

51Testing软件测试网kN4T$[6K{,mc'g*v


/W\DFt0Animal::~Animal() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //手工定义析构函数的必要性&nbsp;51Testing软件测试网(y0as xC*^J
{51Testing软件测试网)Y5PE6A&U0s'J
&nbsp; &nbsp; cout << "(析构函数被调用)\n";51Testing软件测试网lQ0J0]Ub(^
&nbsp; &nbsp; delete m_pName;<SPAN style="WHITE-SPACE: pre">      </SPAN>//释放内存空间&nbsp;51Testing软件测试网4vW IbR0{zh
}

T&y&U8VE!S?s0

;uM$?'KNP_)Aj5?051Testing软件测试网qZ)bo#o
Animal::Animal(const Animal& c) &nbsp; &nbsp; &nbsp; &nbsp;<SPAN style="WHITE-SPACE: pre"> </SPAN>//手工定义拷贝构造函数的必要性&nbsp;51Testing软件测试网N?f ?q*w;IP!^
{
t#cbN0uCF4r8ir0&nbsp; &nbsp; cout << "(拷贝构造函数被调用)\n";51Testing软件测试网(O)t7~9`3^?
&nbsp; &nbsp; m_pName = new string(*(c.m_pName));<SPAN style="WHITE-SPACE: pre"> </SPAN>//实现“深拷贝”的关键&nbsp;51Testing软件测试网a,g Ykj6jvq Z
&nbsp; &nbsp; m_Age = c.m_Age;51Testing软件测试网B _'L{ Ae4dtl-o;R
}
51Testing软件测试网7VMkq'ln@

51Testing软件测试网b"[:iB"Z&R(d


.mqL*y&[0Animal& Animal::operator=(const Animal& c) &nbsp;//手工定义操作符重载的必要性&nbsp;51Testing软件测试网-{i0yG bn v0F
{51Testing软件测试网Ge.y2?%B @_#V
&nbsp; &nbsp; cout << "(重载操作符被调用)\n";51Testing软件测试网xWI5H{@V m
&nbsp; &nbsp; if (this != &c)
&^&AEH+f,P0&nbsp; &nbsp; {51Testing软件测试网8qc'[!?3h.f]B
&nbsp; &nbsp; &nbsp; &nbsp; delete m_pName;//别忘了释放掉原先的内存51Testing软件测试网n)l j4w9r I
&nbsp; &nbsp; &nbsp; &nbsp; m_pName = new string(*(c.m_pName));
.merY'Z[:~!r4s%x0&nbsp; &nbsp; &nbsp; &nbsp; m_Age = c.m_Age;
[/s3ja'o0&nbsp; &nbsp; }51Testing软件测试网i.}9?)[s,wP
&nbsp; &nbsp; return *this;
f,O5X"wHTv1RLN0}
51Testing软件测试网|5@"sQCt

51Testing软件测试网7R;brF#E7l%M

51Testing软件测试网.U5r$TokLWW P
void Animal::Greet() const51Testing软件测试网] }:j`(UgSl!]`
{
G!` \ \l']}UB]0&nbsp; &nbsp; &nbsp;cout << "你好,我叫" << *m_pName << " ,我今年" << m_Age << "岁了。 ";51Testing软件测试网#D5J:jk H ?_%o D,p\/t c
&nbsp; &nbsp; &nbsp;cout << "&m_pName: " << cout << &m_pName << endl;
eb$cyD0}
51Testing软件测试网Rc(H%V-\vmL

51Testing软件测试网;S zEY'{hf)m2T

51Testing软件测试网&iPh!Ei J+td"C
//声明3个测试函数&nbsp;
1?:Z`+^d7q Ts0void testDestructor();51Testing软件测试网(I!cA5Z ]-U8t{
void testCopyConstructor(Animal aCopy);
"er J,WWl0void testAssignmentOp();
51Testing软件测试网-? O.O$V9MC2n.b,{

+[f~2D?:Qh,o2e051Testing软件测试网 o"O x i&C
int main()
h5w\ S/e8j/\ V0{
7kP7g*fR0&nbsp; &nbsp; testDestructor();
F!mI](OwVa M0&nbsp; &nbsp; cout << endl;
i*U)S{w:ti6Jc5{ R0&nbsp; &nbsp;&nbsp;51Testing软件测试网F+T"b:M*s/z z
&nbsp; &nbsp; Animal crit("Poochie", 5);51Testing软件测试网F/\j)A}9[
&nbsp; &nbsp; crit.Greet();
v8g`.J n"Tl5z{|X#a0&nbsp; &nbsp; testCopyConstructor(crit);
+}aN4g T w,n#|0&nbsp; &nbsp; crit.Greet();51Testing软件测试网-y ZFW} P;N7t#_U!h
&nbsp; &nbsp; cout << endl;51Testing软件测试网I `V4C }}
&nbsp; &nbsp;&nbsp;51Testing软件测试网V"SaCi{
&nbsp; &nbsp; testAssignmentOp();

3gW\ |q,A,X$V0

RqTtx[051Testing软件测试网,OrT1gyl
&nbsp; &nbsp; return 0;51Testing软件测试网kQ[l1~bO`6@6G
}

J)]o9F2|o+E[B051Testing软件测试网~M']0]z

51Testing软件测试网\;o;HvNT
void testDestructor()51Testing软件测试网V'iik#b*Z2X){
{
lIB }(l d-t!p5lf0&nbsp; &nbsp; Animal toDestroy("Rover", 3);
,c$w-XR1EQJj#o0&nbsp; &nbsp; toDestroy.Greet();51Testing软件测试网tyih\
}<SPAN style="WHITE-SPACE: pre"> </SPAN>//运行结束时,toDestroy对象随即被销毁&nbsp;
51Testing软件测试网U;J#b4h$`,X.gj4?q

SWR6z |0
g1by,c3Ig y0void testCopyConstructor(Animal aCopy) &nbsp;//注:不是引用类型&nbsp;51Testing软件测试网y0k]ab`
{51Testing软件测试网)t5O-q{Zy@L
&nbsp; &nbsp; aCopy.Greet();51Testing软件测试网Nvc;X3yPWjfGz$Bv
}
51Testing软件测试网rA6N N _ {J-e$O

51Testing软件测试网#}+d1x#v(|K%j]

51Testing软件测试网1V8Qw@6J F!RRA
void testAssignmentOp()51Testing软件测试网 XU;uue:H
{
t#g Dn3CV!R?@K k0&nbsp; &nbsp; Animal ani1("ani1", 7);
}]T}#IQ f0&nbsp; &nbsp; Animal ani2("ani2", 9);
j6`RJ1L6@3|;Q0&nbsp; &nbsp; ani1 = ani2;51Testing软件测试网Wc/BAO9X![U
&nbsp; &nbsp; ani1.Greet(); &nbsp;51Testing软件测试网4Tv ~!w#SOFlGH
&nbsp; &nbsp; ani2.Greet();
N;X9l:n9i0&nbsp; &nbsp; cout << endl;51Testing软件测试网Y Fn uF+p
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;
:\J{uD$Z7f{DG0&nbsp; &nbsp; Animal ani3("ani", 11);51Testing软件测试网5~g0rfq)x4ON o
&nbsp; &nbsp; ani3 = ani3;
,elg6v;{;L8d n0&nbsp; &nbsp; ani3.Greet();51Testing软件测试网3m5`I;[4j9e6dK0{
}

8L+ooft%^$qp H.A0

Ic.H$G!H)W7Sb0  运行结果:51Testing软件测试网 Ur#X)]$`1N"C

51Testing软件测试网 df/[GO

  可以发现:Animal对象被销毁、复制以及相互赋值是,对这些数据成员做出了不同的处理,具体分析如下。51Testing软件测试网 zM ^ SPku3@

  析构函数51Testing软件测试网5d&aK:t7XQ&v/{m

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

"F7a"tooy0

51Testing软件测试网$NH#A4od9@a/Ym

    delete m_pName;      //释放内存空间

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

  总之:如果在堆中分配内存,则应当编写析构函数来清理与释放堆中的内存。51Testing软件测试网"~*\WxJG

  拷贝构造函数51Testing软件测试网l O(CFxj0n0W

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

m0q0A ^v6l,cg~0
    m_pName = new string(*(c.m_pName)); 

  观察程序,发现当testCopyConstructor()时使用的m_pName堆中地址与main中crit 使用的m_pName地址并不相同,这就实现了堆内存数据的复制。当testCopyConstructor()执行结束时,析构函数被调用,释放了对象 内存。

1`-a@)b6b0

  总之:当类的数据成员指向堆中内存时,应当考虑编写手动拷贝构造函数来为新对象分配内存,实现深拷贝。

Q"D `8lz f Q/\.CV0

  赋值运算符的重载

+K&y ]po0

  同上述几个函数一样,如果程序员没有编写自己的赋值运算符成员函数,编译器就会为程序员提供一个默认的成员函数,但这也是相当简单的。

2nX9s1rS0

  如果只是用默认的赋值运算符成员函数,也只是实现的浅拷贝。所以Animal的赋值运算符成员函数应当写成:

f1N"g/K:uN\(V0

51Testing软件测试网1Pd/DtH*u.i

Animal& Animal::operator=(const Animal& c)  //手工定义操作符重载的必要性51Testing软件测试网4b\R}.\1?^4]6i
{
$s@"F f | K W$u0    cout << "(重载操作符被调用)\n";51Testing软件测试网-e8x.]4@*N
    if (this != &c)
3AJ EO_+p/LM0    {51Testing软件测试网3NDPF m8u bT
        delete m_pName;//别忘了释放掉原先的内存
a @h O$ab_0        m_pName = new string(*(c.m_pName));
%{U\ck6?0        m_Age = c.m_Age;51Testing软件测试网Im!e]!n8a~,kg
    }51Testing软件测试网#f|b:G_
    return *this;51Testing软件测试网8y??w/}e c%Fj
}

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

WFy SKSDf0

  总之:当类中有数据成员指向堆中内存时,应当考虑为该类重载赋值运算符。51Testing软件测试网 w@c.z4o*l!~W ^PV

  本文出处:http://write.blog.csdn.net/postedit/8046379

TH3a R#JO+U0

TAG:

 

评分:0

我来说两句

Open Toolbar