本节书摘来自异步社区出版社《C++面向对象高效编程(第2版)》一书中的第4章,第4.4节,作者: 【美】Kayshav Dattatri,更多章节内容可以访问云栖社区“异步社区”公众号查看。
4.4 对象的标识
C++面向对象高效编程(第2版)
本节将介绍命名对象的概念。特别地,我们要明辨对象的名称、对象的标识和对象间共享的语义。
对于这个例子(见图4-4),回顾TPerson类。以下一段代码创建了一些TPerson类对象。
图4-4
main()
{
TPerson person0(“Albert Einstein”, 0, 0, “12-11-1879”);
TPerson person1(“12-11-1879”);
TPerson* person2 = new TPerson(“10-11-95”); // 动态对象
TPerson* person3 = new TPerson(“6-27-87”);
TPerson* person4 = 0; // 未指向person
TPerson* person5 = 0; // 未指向person
person1.SetName(“Albert Einstein”);
person2->SetName(“Foo Bar”);
person4 = person3; // 参见图4-4
}```
显然,对象person1是一个独立的对象,它的名称为person1。但是,person2不是对象真正的名称,它表示内存中另外创建的一个无名称的对象。类似地,person3也表示内存中无名称的另一个对象。在涉及person2所表示的对象名时,我们可以通过 *person2间接地表示的该对象名。在该例中,识别对象很容易,但并不是通过它们的名称来识别。严格来说,只有person0和person1是对象的名称。而person2、person3是指向内存中匿名对象的指针,person4和person3都表示相同的对象。该例中的person2和person3通过不同的状态识别不同的对象。按照这样的思路,person1是具有不同状态且名称不同的对象。
对象的名称在它的整个生存期内可以不唯一,但该对象的标识必须唯一。对象的标识不能随着对象状态的改变而改变。根据[Booch94],对象的标识是该对象区别于其他对象的性质。如上所述的程序和图中,在程序的执行期内,在内存中创建的匿名对象获得了person3和person4名称。我们可以把person4从与之相关的对象中分离出来,但是却不能移除对象的标识——每个对象都拥有一个独一无二的标识,在其生存期内绝不会改变。
通过对象person1执行SetName方法,将改变它的状态,但不会改变它的标识。与此类似,通过person2所表示的对象执行SetName也不会改变它的标识。在对象的整个生存期内,该对象可以获得不同的名称和经历许多状态的改变,但它只有一个独一无二的标识。这与火车站非常类似,每个火车站都拥有唯一的标识,许多火车可以在火车站来来往往(状态改变),但火车站的标识不会改变。换言之,在对象的生存期内,可以通过多个名称引用该对象,但该对象的标识是独一无二的。
看看执行如下语句将发生什么情况:
`person5 = person3;`
记住,person3指定了一个唯一的对象。此时,该对象获得了一个名为person5的别名(见图4-51)。
现在,如果我们操作person3或person5所表示的对象,实际上是在操作同一个对象。虽然名称不同(person4、person5和person3),但对象的标识相同。实际上,我们现在已经在三个名称之间共享了一个对象,即共享了对象的结构(因此也共享了状态)。从图4-5中不难发现,person4和person5都是最初由person3所表示对象的别名。
注意,两个对象的状态可能完全一样(如person0和person1),但是它们并不是相同的对象,因为它们的标识不同。它们占用不同的内存,因而位于不同的内存地址。
你可能想知道,为什么区别标识和名称如此重要?为了妥善管理对象无内存泄漏,必须清楚地知道正在被访问对象的标识是什么。悬挂引用是一个对象有多个名称(别名)直接导致的结果,这也有可能引起内存泄漏。
![image](https://yqfile.alicdn.com/8a18127136ff328262102fafe7f8bb319933758a.png)
图4-6
如果运行以下代码(见图4-6):
delete person4;
person4 = 0;`
显然,此时person3和person5成为悬挂引用,而且我们并未删除person4。另一方面,如果执行下一页的几条语句(如图4-7所示)。
图4-7
person4 = &person1;
person3 = person2;
person5 = 0;```
我们已经创建了无用单元,因为最初由person3、person4和person5所表示的对象已经无法找到。该对象仍然是正在运行程序的一部分,但是,无论怎样都无法再访问它。这是由于未充分理解对象名和对象标识之间的区别所导致的后果。
如此看来,共享对象有错?当然不是。在很多情况下,共享对象是首选的方案。但是,在共享对象时,必须充分理解并正确管理对象的别名。如果未能正确遵循共享对象的原则,便会产生悬挂引用、内存泄漏(无用单元)和无法预料的对象状态改变。悬挂引用在运行时就会暴露出严重的问题,而内存泄漏则需要时间的积累才会引发问题。在本章中还能见到共享对象的不同样式。更多共享对象的技术将在本书的其他章节中讲解。
1译者注:原图4-5有误,已修正。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。