14.6 派生类的数组
这个被广泛描述的缺陷[Meye1996, Stro1997, Dewh2003]是C++(www.cppentry.com)最有问题的imperfections之一。如果你有一个父类Base,一个派生类Derived,它们的大小不同(也就是说,Derived的实例比Base的大),那么将Derived数组的指针传递给一个接受Base数组指针的函数就会导致不愉快的结果。因为这样一来除了数组的第一个元素(偏移量为0)能够对齐外,其余所有元素都不会对齐。这种情况下能够预期的最好结果就是一个干净利落的程序崩溃。考虑程序清单14.4中的代码:
程序清单14.4
- struct Base
- {
- Base()
- : m_i0(0)
- {}
- int m_i0;
- };
-
- void print_Base(Base &b)
- {
- printf("%d ", b.m_i0);
- }
-
- class Derived
- : public Base
- {
- Derived()
- : m_i1(1)
- {}
- int m_i1;
- };
-
- void print_array(Base ab[], size_t cb)
- {
- for(Base *end = ab + cb; ab != end; ++ab)
- {
- print_Base(*ab); // 处理每一个元素
- }
- }
-
- int main()
- {
- Base ab[10];
- Derived ad[10];
- print_array(ab, 10); // Ok
- print_array(ad, 10); // 编译通过并运行,但错误就在前方不远处守候着您!
- . . .
在本例中,对print_array()的第一次调用会正确地打印出"0 0 0 0 0 0 0 0 0 0",然而第二次调用却会打印出"0 1 0 1 0 1 0 1 0 1"。实际上这比程序崩溃还要糟糕,因为如果程序的"病症"是像本例中的这种"良性"的话,bug就有可能引不起你的注意。幸运的是,在大多数实际的情况下,这种误用所导致的结果是程序崩溃。
人们可以争辩说这根本不能算是个imperfection, 只不过是C++(www.cppentry.com)对象模型所导致的结果而已[Lipp1996]。但它被忽视和误解的程度是如此频繁,导致的后果是如此危险,且编译器对其无法提供任何防卫性的措施,因此在我的评价中它被看成一个严重的imperfection。
Imperfection:C++(www.cppentry.com)的数组/指针二重性,加之对派生类型的多态性支持,导致了一个危险,并且编译器对此不提供任何帮助措施。
在本节的剩余部分,我们将会看到有关这个缺陷的几个局部的解决方案,以及用于避免该缺陷的技术,最后我们将看到一种用于表示数组形参的充分有效的技术。