标签:
char boy[] = "Danny";
char *p_son;
...
p_son = new char[strlen(boy) + 1];
strcpy(p_son, boy);
...
if (!strcmp(p_son, boy))
take_to_disneyland(boy);
String girl = "Anna";
String daughter;
...
// String::operator=();
daughter = girl;
...
// String::operator==();
if (girl == daughter)
take_to_disneyland(girl);
void check_in(Library_materials *pmat)
{
if (pmat->late())
pmat->fine();
pmat->check_in();
if (Lender *plend = pmat->reserved())
pmat->notify(plend);
} 纯粹以一种paradigm写程序,有助于整体行为的良好巩固,然而如果混合了不同的paradigms,就可能会带来让人惊吓的后果,特别是在没有谨慎处理的情况下。最常见的疏忽发生在以一个base class的具体实体如:Library_materials thing1;来完成某种多态(polymorphism)局面时:
class Book : Public Library_materials {...};
Book book;
// thing1不是一个Book,book被切割(sliced),不过thing1仍保有一个Library_materials.
thing1 = book;
// 调用Library_materials::check_in()
thing1.check_in(); 而不是通过base class的pointer或reference来完成多态局面:Library_materials &thing2 = book; thing2.check_in();虽然可以直接或间接处理继承体系中的一个base class object,但只有通过pointer或者reference的间接处理,才支持OO程序设计所需的多态性质。上个例子中的thing2的定义和运用,是OO paradigm中一个良好的例证。thing1的定义和运用则逸出了OO的习惯,它反映的是一个ADT paradigm的良好行为。thing1的行为是好是坏,视程序员的意图而定。
// 没有多态,因为操作对象不是class object int *pi; // class x视为一个base class(可以有多态的效果) x *px;在C++,多态只存在与一个个的public class体系中,例如px可能指向自我类型的一个object,或者指向以public派生而来的一个类型。Nonpublic的派生行为以及类型为void*的指针可以说是多态,但它们并没有被语言明白地支持,也就是说它们必须由程序员通过显式的转换操作来管理。
shape *ps = new circle();
ps->rotate();
if (circle *pc = dynamic_cast<circle *>(ps))多态的主要用途是经由一个共同的接口来影响类型的封装,这个接口通常被定义在一个抽象的base class 中。例如Library_materials class 就为Book、Vedio等subtype定义了一个接口。这个共享接口是以virtual function机制引发的,它可以在执行期根据object的真正类型解析出到底是哪一个函数实体被调用,经由这样的操作:
Library_material->check_out();代码可以避免由于"借助某一特定library的materials"而导致变动无偿,这不只使得"当类型有所增加、修改、删减时,程序代码不需改变",而且也使一个新的Library_materials subtype的供应者不需要重新编写出"对继承体系中所有类型都共通"的行为和操作.
void rotate(X datum, const X *pointer, const X &reference) {
// 在执行期之前,无法决定到底调用哪一个rotate()实体
(*pointer).rotate();
reference.rotate();
// 下面这个操作总是调用X::rotate()
datum.rotate();
}
main() {
Z z; // Z是X的一个子类型
rotate(z, &z, z);
return 0;
} 经由pointer和reference完成的两个"函数调用指针"会被动态完成。此例中它们都调用Z::rotate(),经由datum完成的"函数调用操作"则可能(可能不)经由virtual机制。不过,它总是调用X::rotate()。(这就是所谓的"编译素养"问题:不管经由datum所调用的virtual function采不采用virtual机制,从语意来说,结果都是一样的)class ZooAnimal {
public:
ZooAnimal();
virtual ~ZooAnimal();
// ...
virtual void rotate();
protected:
int loc;
string name;
};
ZooAnimal za("Zoey");
ZooAnimal *pza = &za; 其中的class object za和指针pza的可能布局为loc(1000~1003),name(1004~1011),virtual(1012~1015)。ZooAimal *px; int *pi; Array<string> *pta;以内存需求的观点来说,没有不同。它们三个都需要有足够的内存来存放一个机器地址。"指向不同类型的指针"间的差异,既不在于指针表示法不同,也不在于其内容(代表一个地址)不同,而是在于其所寻址出来的object类型不同。也就是说,"指针类型"会指示编译器如何解释某个特定地址中的内存内容以及其大小。
class Bear : public ZooAnimal {
public:
Bear();
~Bear();
// ...
void rotate();
virtual void dance();
// ...
protected:
enum Dances {...}
Dances dances_known;
int cell_block;
};
Bear b("Yogi");
Bear *pb = &b;
Bear &rb = *pb; b,pb,rb会有怎样的内存需求呢?不管是pointer或reference都只需要一个word的空间(在32位机器上是4-bytes)。Bear object需要24 bytes,也就是ZooAnimal的16 bytes加上Bear所带来的8 bytes。 Bear b; ZooAnimal *pz = &b; Bear *pb = &b;它们每个都指向Bear object的第一个byte,其间的差别是pb所涵盖的地址包含整个Bear object,而pz所涵盖的地址只包含Bear object中的ZooAnimal subobject。
// 不合法:cell_block不是ZooAnimal的一个member,虽然pz当前指向一个Bear object
pz->cell_block;
// OK:经过一个显示的downcast操作就没问题
((Bear *)pz)->cell_block;
// 下面这样更好,但它是一个run-time operation(成本较高)
if (Bear *pb2 = dynamic_cast<Bear *>(pz))
pb2->cell_block;
// OK:因为cell_block是Bear的一个member
pb->cell_block; 如果写如下语句pz->rotate();时,pz的类型将在编译时决定以下两点:
Bear b; ZooAnimal za = b; // 这会引起切割(sliced) // 调用ZooAnimal::rotate() za.rotate();为什么rotate()所调用的是ZooAnimal实体而不是Bear实体?此外,如果初始化函数(应用于上述assignment操作发生时)将一个object内容完全拷贝到另一个object中去,为什么za的vptr不指向Bear的virtual table?
{
ZooAnimal za;
ZooAnimal *pza;
Bear b;
Panda *pp = new Panda;
pza = &b;
} 将za或b的地址,或pp的内容(也是个地址)赋给pza,显然不是问题。一个pointer或者一个reference之所以支持多态,是因为它们并不引发内存中任何"与类型有关的内存委托操作(type-dependent commitment)",会受到改变的知识它们所指向的内存的"大小和内容解释方式"而已。版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/yiranant/article/details/47121893