C++中使用基类指针操作子类数组的分析
2008-03-29 21:10
936 查看
基础知识:
Ø 数组:在C/C++中,数组是一块连续的内存,内存中数组元素紧密地排列在一起。内存的大小 = 元素个数 x 单个元素的大小。
Ø 操作符[]:使用索引访问数据中的元素。元素在内存中的位置为:数组开始位置 + 索引 x单个元素的大小。
Ø 类的实例:类的实例也占用内存。类占内存的大小和类的数据成员有关,类的数据成员越多,那么占用的内存也越大。
Ø 子类和父类的内存分配:父类中的数据成员在前,子类的数据成员在后。
Ø 指针的加减法:当指针加减一个整数时,实际的指针位置加减量为:该整数 * 指向类型的大小。如int *p; p+1的结果是p指向位置后的第4个字节。
Ø delete []:删除数据。从指针位置开始删除数组元素,一直到内存块结束。对于类数组,会对每个元素调用析构函数。
子类数组:
基于以上知识,子类数组的内存分布可以粗略表示为下图:
使用父类指针时的操作分析:
把一个子类数组变量赋值给一个数据元素父类的指针时,指针首先指向数组头的位置(如图)。
操作1:访问元素
访问第一个元素(元素0)时,没有任何问题。
访问下一个元素时,指针后移。按正常用法使用++操作或+=1操作。因为该指针是父类指针,所以偏移量为父类的大小sizeof(父类)。这时指针将指向图中“子类数据”的位置,而不是元素1的位置。这将导致出错。使用[]操作访问时也有同样的问题。
b538
操作2:使用delete[]删除数据:
delete[]同样是按访问元素的方法逐个析构这些元素。因为它无法找到对象真正的起始位置。
特殊情况:
在CSDN的讨论中,有人写出了验证代码,证明这样访问没有问题,如Chappell的代码:
#include <iostream.h>
class Base
{
public:
Base()
{
cout<<"Base()"<<endl;
}
virtual ~Base()
{
cout<<"~Base()"<<endl;
}
};
class Child:public Base
{
public:
Child()
{
cout<<"Child()"<<endl;
}
virtual ~Child()
{
cout<<"~Child()"<<endl;
}
};
int main(int argc, char* argv[])
{
Base* pBase = new Child[3];
delete []pBase;
return 0;
}
这段代码可以正确运行,但与刚才所说的并不矛盾。这段代码中的Child类属于特例,因为它没有数据成员,子类数据是空的。这时如果使用sizeof来检查Child类和Base类的大小,会发现它们是相同的。所以无论使用子类指针还是父类指针,元素定位的计算结果都一样。
总结:
不要用父类指针去操作子类数组,虽然你可能碰巧不出错。
作者:苏林
Ø 数组:在C/C++中,数组是一块连续的内存,内存中数组元素紧密地排列在一起。内存的大小 = 元素个数 x 单个元素的大小。
Ø 操作符[]:使用索引访问数据中的元素。元素在内存中的位置为:数组开始位置 + 索引 x单个元素的大小。
Ø 类的实例:类的实例也占用内存。类占内存的大小和类的数据成员有关,类的数据成员越多,那么占用的内存也越大。
Ø 子类和父类的内存分配:父类中的数据成员在前,子类的数据成员在后。
Ø 指针的加减法:当指针加减一个整数时,实际的指针位置加减量为:该整数 * 指向类型的大小。如int *p; p+1的结果是p指向位置后的第4个字节。
Ø delete []:删除数据。从指针位置开始删除数组元素,一直到内存块结束。对于类数组,会对每个元素调用析构函数。
子类数组:
基于以上知识,子类数组的内存分布可以粗略表示为下图:
使用父类指针时的操作分析:
把一个子类数组变量赋值给一个数据元素父类的指针时,指针首先指向数组头的位置(如图)。
操作1:访问元素
访问第一个元素(元素0)时,没有任何问题。
访问下一个元素时,指针后移。按正常用法使用++操作或+=1操作。因为该指针是父类指针,所以偏移量为父类的大小sizeof(父类)。这时指针将指向图中“子类数据”的位置,而不是元素1的位置。这将导致出错。使用[]操作访问时也有同样的问题。
b538
操作2:使用delete[]删除数据:
delete[]同样是按访问元素的方法逐个析构这些元素。因为它无法找到对象真正的起始位置。
特殊情况:
在CSDN的讨论中,有人写出了验证代码,证明这样访问没有问题,如Chappell的代码:
#include <iostream.h>
class Base
{
public:
Base()
{
cout<<"Base()"<<endl;
}
virtual ~Base()
{
cout<<"~Base()"<<endl;
}
};
class Child:public Base
{
public:
Child()
{
cout<<"Child()"<<endl;
}
virtual ~Child()
{
cout<<"~Child()"<<endl;
}
};
int main(int argc, char* argv[])
{
Base* pBase = new Child[3];
delete []pBase;
return 0;
}
这段代码可以正确运行,但与刚才所说的并不矛盾。这段代码中的Child类属于特例,因为它没有数据成员,子类数据是空的。这时如果使用sizeof来检查Child类和Base类的大小,会发现它们是相同的。所以无论使用子类指针还是父类指针,元素定位的计算结果都一样。
总结:
不要用父类指针去操作子类数组,虽然你可能碰巧不出错。
作者:苏林
相关文章推荐
- 不要对用基类指针操作子类数组
- c++中使用基类指针Vector 存放子类对象(0xcccccccc 处未处理的异常: 0xC0000005: 读取位置 0xcccccccc 时发生访问冲突 )
- C++ 虚函数和基类指针数组应用
- C++中子类的数组不能用父类指针来表示
- 约瑟夫环问题的PHP实现 使用PHP数组内部指针操作函数
- C++通过基类指针delete派生类数组,析构函数是虚函数,程序为什么会崩溃? https://www.zhihu.com/question/30838092/answer/49623765
- 谈谈基类与子类的this指针(C++)
- C++ 基类、派生类对象指针的声明与使用
- Java中JNI的使用详解第五篇:C/C++中操作Java中的数组
- C++学习计划之指针和多维数组的操作
- c/c++字符指针与字符数组变量汇编层面的分析
- C++第8周项目 数组和指针 【项目2-用对象数组操作长方柱类】(复习新版)
- C++虚函数表解析(图文并茂,非常清楚)( 任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法)good
- C/C++指针的指针(**p)和指针的引用(*&)使用案例分析
- C++中使用基类指针调用派生类中定义的方法
- C++ 二级指针、函数指针与数组复合类型的问题分析
- 2. 使用指针操作数组
- c++中基类与派生类中隐含的this指针的分析
- 在C++中,要实现动态联编,必须使用(基类指针)调用虚函
- C++第八周mooc在线测评—第8周 按址操作(2)——指针与数组、字符串、结构体,动态数组