【More Effective C++ 条款3】最好不要以多态方式处理数组

1.在数组与多态混用的情况下,数组元素的访问会出现不可预期的结果(因为数组元素的访问会使用到下标运算)

将一个子类对象数组传递给一个父类对象数组声明的函数,编译器会允许这个行为,但是由于子类对象和父类对象的内存结构不同,会导致运行结果异常,因为在这种情况下,编译器仍然假设每一个元素的大小是父类对象元素的大小,但此时实际上每一个元素的大小是子类对象元素的大小

#include<bits/stdc++.h>
using namespace std;

class BST
{
public:
    char c;
};

class balancedBST:public BST//公有继承
{
public:
    int x;
};

void printBSTArray(ostream& s,const BST array[],int numElements)//打印基类数组,注意数组的类型为基类!
{
    for(int i=0;i<numElements;i++)
        s<<array[i].c;
    s<<endl;
}

int main()
{
    BST array[3];
    for(int i=0;i<3;i++)
        array[i].c=‘A‘+i;

    printBSTArray(cout,array,3);//传递进入函数的是基类数组,打印正常

    balancedBST barray[3];
    for(int i=0;i<3;i++)
    {
        barray[i].x=i+1;
        barray[i].c=‘a‘+i;
    }

    printBSTArray(cout,barray,3);//传递进入函数的是子类数组,打印异常!
    /*
    异常的原因:
    因为函数定义的时候该对象的数组定义的是为基类类型,所以编译器在后续的操作中,
    无论传入函数的是基类对象数组还是子类对象数组,都将视为基类对象数组
    而基类对象的内存结构和子类对象的内存结构是不一样的
    这样,通过下标访问到的东西,其实不是你想访问的东西

    array[i] *(array+i)
    array[0]和array[i] 之间相隔 i*sizeof(BST)

    函数是按照i*sizeof(BST)算的,而如果你传入的是基类对象数组,也按照这样算的话,肯定是访问不到你想要的东西的
    */

}

/*
运行结果:
ABC
ai
*/

2.在多态与数组混用的情况下,数组元素的删除也存在问题,会使用到没有定义的操作(通过基类指针来删除子类对象数组的行为在C++中是未定义的!)

#include<bits/stdc++.h>
using namespace std;

class BST
{
public:
    char c;
};

class balancedBST:public BST//公有继承
{
public:
    int x;
};

void deleteArray(ostream& logStream,BST array[])
{
    logStream<<"Deleting array at adress: "<<static_cast<void*>(array)<<endl;
    delete [] array;//该操作的底层其实使用了下标,如果传入的是子类对象数组,那么通过基类指针来删除子类对象数组的行为在C++中是未定义的!
}

int main()
{
    BST *array =new BST[3];
    balancedBST *barray =new balancedBST[3];

    deleteArray(cout,array);//传入的是基类对象

    deleteArray(cout,barray);//传入的是子类对象,存在问题,底层使用了未定义的操作
}

总结:那么如何避免上述两种问题呢?

第一个办法是避免采用多态方式来处理数组

第二个方法是避免让一共具体类继承自另外一个具体类

原文地址:https://www.cnblogs.com/yinbiao/p/11747782.html

时间: 2025-01-17 22:25:27

【More Effective C++ 条款3】最好不要以多态方式处理数组的相关文章

More Effective C++ 条款35 让自己习惯于标准C++ 语言

(由于本书出版于1996年,因此当时的新特性现在来说可能已经习以为常,但现在重新了解反而会起到了解C++变迁的作用) 1. 1990年后C++的重要改变 1). 增加了新的语言特性:RTTI,namespaces,bool,关键词mutable和explicit,enums作为重载函数之自变量所引发的类型晋升转换,以及"在class 定义区内直接为整数型(intergral) const static class members设定初值"的能力. 2). 扩充了Templates的特性

effective c++ 条款4 make sure that objects are initialized before they are used

1 c++ 类的数据成员的初始化发生在构造函数前 class InitialData { public: int data1; int data2; InitialData(int a, int b) { data1 = a: //this is assignment data2 = b; //this is assignment } /* InitialData(int a, int b):data1(a),data2(b) //this is initial {} */ } 2 不同cpp文

More Effective C++ 条款34 如何在一个程序中结合C++和C

1. C++和C混合使用的前提之一就是编译器产生兼容的目标文件(.lib和.dll等).所谓"兼容",指的是编译器在"预编译器相依的特性上"一致,如int和double大小,参数压栈机制等,只有在这个基础上才能讨论结合使用C++和C模块的问题. 2. 在1的基础上,要结合使用C++和C的模块,主要有以下几点需要注意: 1). name mangling(名称重整) Name mangling是C++用于支持函数重载的机制,它对重载的函数名称进行一定改变,使得每个函数

Effective C++ 条款3 尽可能用const

1. const可被施加于任何作用域内的对象,函数参数,函数返回类型,成员函数本体.用const修饰指针,如果const出现在*之前,表明指针不能更改所指向的对象的内容,如果const出现在*之后,表明指针只能指向同一块内存.另外int const*p和const int*p含义相同.如果对象成员有普通指针,那么构造该类的一个const对象时,const修饰使得该指针只能指向同一块内存,但指针指向的内容可以改变. 2. 将某些东西声明为const可以帮助编译器侦测出错误用法. 3. 编译器强制实

effective c++ 条款13 use object to manage resources.

请求的系统资源需要最终还回系统,为了避免遗忘返还这个动作,可以利用析构函数在object销毁时自动调用的特点来实现. 简单说就是用object来管理资源. 以内存资源为例 class Investment {}; Investment* creatInvestment(){...} // factory function to produce investment object void main() { Investment* pInv = creatInvestment();//call t

effective c++ 条款3 use const whereever you can

1 const 传达的意思应该是这个变量是常量不能更改 2 const 在 * 左边表示数据是const,在右边表示指针是const // char greeting[] = "hello"; char* p = greeting; //const *: const data //* const: const pointer char const *p1 = greeting; // const data char * const p2 = greeting; // const poi

effective c++ 条款18 make interface easy to use correctly and hard to use incorrectly

举一个容易犯错的例子 class Date { private: int month; int day; int year; public: Date(int month,int day,int year) { this->month = month; ... } } //wrong example Date date(30,3,1995);//should be 3,30 Date date(2,30,1995);//should be 3,30 使用类型可避免这个问题 class Month

effective c++ 条款9 do not call virtual function in constructor or deconstructor

在构造函数中不要调用virtual函数,调用了也不会有预期的效果. 举个例子 class Transaction { public: Transaction() { log(); } virtual void log() =0; } class BusinessTransaction: public Transaction { public: virtual void log() { ;//log something here } } BusinessTransaction b_trx; b_t

Effective C++ 条款九、十 绝不在构造和析构过程中调用virtual函数|令operator=返回一个reference to *this

  1.当在一个子类当中调用构造函数,其父类构造函数肯定先被调用.如果此时父类构造函数中有一个virtual函数,子类当中也有,肯定执行父类当中的virtual函数,而此时子类当中的成员变量并未被初始化,所以无法调用子类与之对应的函数.即为指向虚函数表的指针vptr没被初始化又怎么去调用派生类的virtual函数呢?析构函数也相同,派生类先于基类被析构,又如何去找派生类相应的虚函数? 2.做法:将子类的某个函数改为non-virtual,然后在子类构造函数中传递参数给父类函数.然后父类的构造函数