我正在尝试编写一个可以调用的类函数,以动态创建新的派生对象,并将该对象添加到动态分配的数组中,该数组存储指向基类对象的指针。如果我两次运行该函数,则可以访问objectAry [0],但是访问objectAry [1]会给我带来读取访问冲突。谁能告诉我为什么这不能适当地存储对象?
objectAry动态分配了10个对象的空间,因此位置1处有空间。即使newObject()仅被调用两次,也会发生这种情况。
//Array definition (outside of function):
Base* objectAry;
objectAry = new Derived[10]
//Function in question:
void Derived::newObject()
{
Derived* tempObject = NULL;
tempObject = new Derived;
objectAry[numObjects] = *tempObject;
numObjects++;
delete tempObject;
tempObject = NULL;
}
运行简单函数以返回派生对象的成员变量之一时:
Exception thrown: read access violation.
this->objectAry+1-> was 0x1.
很显然,我没有正确地存储它,但是我不确定如何使它正常工作。
答案 0 :(得分:0)
首先,参考:
//Array definition (outside of function):
Base* objectAry;
objectAry = new Derived[10];
分配了指向Base的指针,但分配了const 指向类Derived的数组的指针。这不 导致故障,但是将后续代码设置为失败。
关于以下陈述,
objectAry[numObjects] = *tempObject;
编译器将objectAry视为Base类的数组。 sizeof(Base)与sizeof(Derived)不同,这导致确定时出错 指向类Derived动态数组内的索引的指针。如果用不适当的偏移量修改了阵列中的内存,则除了琐碎的示例以外,生成内存故障只是时间问题。
在以下代码中,以某种方式创建了上下文,以重现问题中的内存错误。在注释中,您将看到该分配在何处被使用dynamic_cast操作的分配所替代。现在,dynamic_cast允许正确计算索引偏移。此外,请始终注意对dynamic_cast操作的使用以及赋值运算符的处理。当以这种方式使用基本指针时,建议谨慎。请参阅最后的“警告:”注释。
#include <iostream>
using namespace std;
class Base;
class Base{
public:
Base(){}
Base& operator=(Base& src){
copy_like_objects(this, &src);
return *this;
}
virtual void copy_like_objects(Base* dst, Base* src) = 0;
virtual ~Base(){};
};
class Derived : public Base{
public:
static const int objectAryLength = 10;
static int numObjects;
static Base* objectAry;
Derived();
~Derived(){};
static void newObject();
int data;
void copy_like_objects(Base *dst, Base *src){
*dynamic_cast<Derived*>(dst) = *dynamic_cast<Derived*>(src);
}
Derived& operator=(Derived& src){
data = src.data;
return *this;
}
static void allocate();
static void deallocate();
};
Derived :: Derived(){
data = -1;
}
void Derived :: allocate(){
if(objectAry == nullptr){
objectAry = new Derived[Derived :: objectAryLength];
}
for(int i = 0; i < Derived::objectAryLength; i++){ dynamic_cast<Derived*>(objectAry)[i].data = 0;}
}
void Derived :: deallocate(){
if(objectAry != nullptr){
delete[] dynamic_cast<Derived*>(objectAry);
objectAry = nullptr;
}
}
void Derived::newObject(){
Derived* tempObject = nullptr;
tempObject = new Derived; // The debugger may not step through
// the default constructor, which is called.
// tempObject = new Derived(); // Debugger steps through default constructor.
// At the time of this writing, in the commented statement
// the compiler seems to be computing the sizeof class base
// to evaluated the index into an array of the supposedly
// allocated array of bases classes instead of flagging as an error.
// As a result, The derived class copies data on a missaligned
// Derived object allocation, currupts the array of objects, which then
// is the cause of a subsequent memory fault.
//
// objectAry[numObjects] = *tempObject;
// Using the cast below, fixes the allignment issues and avoid a memory fault.
dynamic_cast<Derived*>(objectAry)[numObjects] = *tempObject;
numObjects++;
delete tempObject;
tempObject = nullptr;
}
int Derived::numObjects = 0;
Base* Derived::objectAry = 0;
int main(int argc, char **argv) {
Derived :: allocate();
for(int i = 0; i < Derived::objectAryLength; i++){
cout << (dynamic_cast<Derived*>(Derived::objectAry))[i].data << " : " ;
} cout << endl;
Derived::newObject();
Derived::newObject();
for(int i = 0; i < Derived::objectAryLength; i++){
cout << (dynamic_cast<Derived*>(Derived::objectAry))[i].data << " : " ;
} cout << endl;
Derived :: deallocate();
return 0;
}
警告:通常,以这种方式使用基类指针时,将导致一种情况,即在dynamic_cast <>期间极有可能发生导致内存故障或异常的错误,这很容易出现在运行时边缘情况下。要解决此问题,请考虑重新设计并开始:
Base** objectAry;
objectAry = new Base*[10];
管理列表的逻辑可以放在基类中,而无需知道哪些派生存储在列表中。这种方法利用了C ++的多态性,将简化编码并提高可靠性。这种方法将允许在列表中管理任何派生的Base类。在列表管理逻辑中正确管理派生对象的分配和释放很重要。