加上封装后的布局成本 layout cost:C的struct 到 C++的class
- data members内含在class object内,和struct一样。
- member functions含在class声明内,但是不出现在object中。
- non-inline member function只会诞生一个函数实例
- 每一个“拥有零个或一个定义”的inline function会在没一个使用者(模块)身上产生一个函数实例
总结:C++在布局以及存取时间上主要的额外负担是virtual引起的,包括:
- virtual function机制:用以支持一个有效率的“执行期绑定”(runtime binding)
- virtual base class:用以实现“多次出现在继承体系中的base class,有一个单一而被共享的实例”
30、struct和class
什么时候应该/可以将C++程序中的class用struct取代?
- struct关键词其实没什么用
- 可以使用struct取代class,但仍然声明public、protected、private等存取区段 与 完全public的接口,以及virtual functions和继承(单一、多重、虚拟)
- struct是一个数据集合体,没有private data,也没有data的操作(member function)。
- 问题:类型层次结构的原始声明中,根节点(root node)和每一个派生下来的子类型(subtype)用struct声明的。但陆续修改的头文件中,某些派生子类型(derived subtypes)的前置声明(forward declaration)却用class。
- class node; struct node { … };
- 不合法?不,只是不一致
- struct实现了C中数据抽象观念,class实现了C++的抽象数据类型ADT观念。
- C++要支持现存的C代码,它就不能不支持struct。C++引入class不仅仅是关键词,而是其所支持的封装和继承的哲学。
31、new和strlen
char *pstr;
pstr = new char[10];
std::cout << strlen(pstr) <<std::endl
这里输出不一定是10。pstr指向的内容没有初始化,已经越界访问了
char boy[] = "Danny";
char *p_son;
p_son = new char[strlen(boy) + 1];
strcpy(p_son, boy);
此外,strlen分配内存是比如 +1。一个字符串的结束标志是’\0’,你不+1能得到相同的字符串,但是这样是很危险的,随时有可能在后面的其他操作中带来巨大的错误!
32、基类和子类
1、通过 基类实例 完成多态。 ADT(抽象数据类型)范式的行为
Library thing1;
// class Book : public Library { ... }; 子类实例
Book book;
// book被裁切(slided)了。不过thing1仍保留一个Library
thing1 = book;
// 调用的是 Library::func()
thing1.func();
2、通过 pointer 或 reference 完成多态。 OO程序设计的多态性质
Library &thing2 = book;
// 调用 Book::func()
thing2.func();
面向对象范式中,被指定的object的真实类型在每一个特定执行点之前是无法解析的。
C++中,只有通过 pointer 或 reference 的操作才能完成。
相反,ADT中处理的是一个拥有固定而单一类型的实例,在编译时期就已经完全定义好了。
// 无法确定px和rx指向何种类型的object,可能是Library object,也可能是后者的子类型(subtype)
Library *px = retrieve_library();
Library &rx = *px;
// 描述已知物。只能是Library object
Library dx = *px;
注意:
C++中,多态只存在于public class体系中。
Nonpublic的派生行为 和 类型为void*的指针 并没有被语言明确支持。他们必须由程序员显示的转换操作来管理。
int *pi; // 不是多态,操作对象不是class obj
void *pvo; // 不是多态,操作对象不是class obj
x *px; // 多态,class x视作一个base class
33、C++多态方法
1、经由隐式转换
shape *ps = new circle();
2、经由 virtual function 机制
ps -> rotate();
3、经由 dynamic_cast 和 typeid 运算符
if ( circle *pc = dynamic_cast<circle*>(ps) ) ...
C++多态的主要用途。经由一个共同的接口来影响类型的封装,这个接口通常被定义在抽象的base class中。
这个共享接口是以virtual function机制引发的,可以在执行期间根据obj的真正类型解析出哪个函数实例被调用。
意义:
-
当类型有所增加、修改、删减时,程序代码无须改变。
-
新的子类型不需要重新写出 “对继承体系中的所有类型都通用” 的行为和操作。
34、一个class object的内存大小
1:nonstatic data member的总和大小
2:alignment所填补的空间,可能存在于members之间,也可能是类的边界
3:为了支持virtual而由内部产生的额外负担,比如:指虚基类表的指针或指向虚函数表的指针
其中1和3都比较好确定,比较难确定的是2中的内存对齐(填补)的大小
35、不同类型的指针有什么不同?怎么区分?
Library *px;
int *pi;
Array<String> *pta;
指针存储的都是一个“机器地址”,通常是个word,其大小不一定,可能是16-bits。
上述指针的差异,不在于指针表示法不同,不在于内容(都表示一个地址)不同,而在于其所寻址出来的object类型不同。
“指针类型”会指导编译器解释某个特定地址的内存内容和其大小。
void *的指针只能持有一个地址,不能通过它操作所指向的object。不知道其涵盖的地址空间。
转换(cast)实际上是一种编译器指令。大部分情况下不改变指针所含的地址,而影响“被指向之内存的大小和其内容”的解释方式。