将动态分配的对象添加到对象指针数组

时间:2019-07-12 22:25:37

标签: c++

我正在尝试编写一个可以调用的类函数,以动态创建新的派生对象,并将该对象添加到动态分配的数组中,该数组存储指向基类对象的指针。如果我两次运行该函数,则可以访问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.

很显然,我没有正确地存储它,但是我不确定如何使它正常工作。

1 个答案:

答案 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类。在列表管理逻辑中正确管理派生对象的分配和释放很重要。