动态分配对象数组 [英] Dynamically allocating an array of objects

查看:115
本文介绍了动态分配对象数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个初学者的问题,但我没有做C ++在很长一段时间,所以这里...



我有一个类,包含一个动态分配的数组,例如

  class A 
{
int * myArray;
A()
{
myArray = 0;
}
A(int size)
{
myArray = new int [size];
}
〜A()
{
//注意,根据MikeB的帮助风格批判,不需要检查0.
delete [] myArray;
}
}

但现在我想创建一个动态分配的数组这些类。这是我当前的代码:

  A * arrayOfAs = new A [5] 
for(int i = 0; i <5; ++ i)
{
arrayOfAs [i] = A(3)
}

但这可怕的。因为 A(3)调用)创建的新的 A > for 循环迭代结束,这意味着 A 实例的内部 myArray 获取 delete [] -ed。



所以我认为我的语法必须非常错误?我想有几个修补程序看起来像过度杀伤,我希望避免:




  • 为<$ c创建一个拷贝构造函数$ c> A 。

  • 使用向量< int>

    A 对象的数组,它是一个 A * 指针的数组。



我认为这只是一些初学者的事情,其中​​有一个语法实际上工作时,试图动态分配内部动态分配的事情数组。



(另外,风格评论赞赏,因为自从我做C ++以来已经有一段时间了。)



观众:以下所有答案都非常有用。马丁的接受,因为示例代码和有用的规则4,但我真的建议阅读他们所有。一些是好的,简明扼要的陈述什么错了,一些指出正确如何和为什么矢量是一个很好的方法去。



如果你的对象有一个RAW指针,那么你需要记住规则




  • 构造函数

  • 析构函数

  • 复制构造函数

  • 分配操作符

  • 移动构造函数(C ++ 11)

  • 移动作业(C ++ 11)



这是因为如果没有定义,编译器会产生自己的版本这些方法(见下文)。编译器生成的版本在处理RAW指针时并不总是有用。



复制构造函数是很难得到正确的(如果你想提供强大的异常保证)。分配操作符可以根据复制构造函数来定义,因为您可以在内部使用复制和交换惯例。



有关类的绝对最小值的完整详细信息,请参见下文包含指向整数数组的指针。



知道要获得正确性是非常简单的,你应该考虑使用std :: vector,而不是指向数组的指针整数。该向量易于使用(和扩展),并涵盖与异常相关的所有问题。将以下类与以下A的定义进行比较。

 类A 
{
std :: vector< ; int> mArray;
public:
A(){}
A(size_t s):mArray(s){}
};

查看您的问题:

  A * arrayOfAs = new A [5]; 
for(int i = 0; i <5; ++ i)
{
//你猜想问题出在这一行。
arrayOfAs [i] = A(3);

//发生了什么:
// 1)A(3)构建你的A对象(罚款)
// 2)A :: operator =(A const& )被调用以将值
//分配给数组访问的结果。因为你做了
//没有定义这个运算符,编译器生成的是
// used。
}

编译器生成的赋值操作符适用于几乎所有情况,都在玩你需要注意。在您的情况下,它会导致一个问题,因为浅拷贝问题。你最终得到两个对象,它们包含指向同一块内存的指针。当A(3)在循环结束时超出范围时,它调用其指针上的delete []。因此,另一个对象(在数组中)现在包含一个指向已经返回给系统的内存的指针。



编译器生成的复制构造函数 ;通过使用该成员副本构造函数复制每个成员变量。对于指针,这只是意味着指针值从源对象复制到目标对象(因此浅拷贝)。



编译器生成的赋值操作符;通过使用成员赋值运算符来复制每个成员变量。对于指针,这只是意味着指针值从源对象复制到目标对象(因此浅拷贝)。



所以包含指针的类的最小值:

  class A 
{
size_t mSize;
int * mArray;
public:
//简单的构造函数/析构函数是显而易见的。
A(size_t s = 0){mSize = s; mArray = new int [mSize];}
〜A(){delete [] mArray;}

复制构造函数需要更多的工作
A(A& copy)
{
mSize = copy.mSize;
mArray = new int [copy.mSize];

//不需要担心复制整数。
//但是如果对象有一个复制构造函数然后
//它也需要担心从复制构造函数的抛出。
std :: copy(& copy.mArray [0],& copy.mArray [c.mSize],mArray);

}

//根据复制构造函数定义赋值运算符
//修改:复制交换惯例有一点小小的扭曲,
//删除通过传递rhs的手动拷贝
//提供由编译器生成的隐式拷贝。
A& operator =(A rhs)//按值传递(从而生成副本)
{
rhs.swap(* this); //现在用副本交换数据。
//当
//超出函数结束时的范围时,rhs参数将删除数组
return * this;
}
void swap(A& s)noexcept
{
使用std :: swap;
swap(this.mArray,s.mArray);
swap(this.mSize,s.mSize);
}

// C ++ 11
A(&& src)noexcept
:mSize(0)
,mArray
{
src.swap(* this);
}
A& operator =(A&& src)noexcept
{
src.swap(* this); //你正在将src对象
//的状态移动到这个对象中。移动后的src对象
//的状态必须有效,但不确定。
//
//最简单的方法是交换状态
//两个对象的状态。
//
//注意:在移动之后对src执行任何操作
//是有风险的(除了destroy),直到将
//放入特定状态。你的对象shoudl有
//适当的方法。
//
//示例:赋值(operator =应该工作)。
// std :: vector()has clear()设置
//一个特定的状态,而不需要
//知道当前状态。
return * this;
}
}


This is kind of a beginners question, but I haven't done C++ in a long time, so here goes...

I have a class that contains a dynamically allocated array, say

class A
{
    int* myArray;
    A()
    {
        myArray = 0;
    }
    A(int size)
    {
        myArray = new int[size];
    }
    ~A()
    {
        // Note that as per MikeB's helpful style critique, no need to check against 0.
        delete [] myArray;
    }
}

But now I want to create a dynamically allocated array of these classes. Here's my current code:

A* arrayOfAs = new A[5];
for (int i = 0; i < 5; ++i)
{
    arrayOfAs[i] = A(3);
}

But this blows up terribly. Because the new A object created (with the A(3) call) gets destructed when the for loop iteration finishes, and this means that the internal myArray of that A instance gets delete []-ed.

So I think my syntax must be terribly wrong? I guess there are a few fixes that seem like overkill, which I'm hoping to avoid:

  • Creating a copy constructor for A.
  • Using vector<int> and vector<A> so I don't have to worry about all this.
  • Instead of having arrayOfAs be an array of A objects, have it be an array of A* pointers.

I would think this is just some beginners thing where there's a syntax that actually works when attempting to dynamically allocate an array of things that have internal dynamic allocation.

(Also, style critiques appreciated, since it's been a while since I did C++.)

Update for future viewers: All of the answers below are really helpful. Martin's is accepted because of the example code and the useful "rule of 4," but I really suggest reading them all. Some are good, succinct statements of what's wrong, and some point out correctly how and why vectors are a good way to go.

解决方案

For building containers you obviously want to use one of the standard containers (such as a std::vector). But this is a perfect example of the things you need to consider when your object contains RAW pointers.

If your object has a RAW pointer then you need to remember the rule of 3 (now the rule of 5 in C++11).

  • Constructor
  • Destructor
  • Copy Constructor
  • Assignment Operator
  • Move Constructor (C++11)
  • Move Assignment (C++11)

This is because if not defined the compiler will generate its own version of these methods (see below). The compiler generated versions are not always useful when dealing with RAW pointers.

The copy constructor is the hard one to get correct (it's non trivial if you want to provide the strong exception guarantee). The Assignment operator can be defined in terms of the Copy Constructor as you can use the copy and swap idiom internally.

See below for full details on the absolute minimum for a class containing a pointer to an array of integers.

Knowing that it is non trivial to get it correct you should consider using std::vector rather than a pointer to an array of integers. The vector is easy to use (and expand) and covers all the problems associated with exceptions. Compare the following class with the definition of A below.

class A
{ 
    std::vector<int>   mArray;
    public:
        A(){}
        A(size_t s) :mArray(s)  {}
};

Looking at your problem:

A* arrayOfAs = new A[5];
for (int i = 0; i < 5; ++i)
{
    // As you surmised the problem is on this line.
    arrayOfAs[i] = A(3);

    // What is happening:
    // 1) A(3) Build your A object (fine)
    // 2) A::operator=(A const&) is called to assign the value
    //    onto the result of the array access. Because you did
    //    not define this operator the compiler generated one is
    //    used.
}

The compiler generated assignment operator is fine for nearly all situations, but when RAW pointers are in play you need to pay attention. In your case it is causing a problem because of the shallow copy problem. You have ended up with two objects that contain pointers to the same piece of memory. When the A(3) goes out of scope at the end of the loop it calls delete [] on its pointer. Thus the other object (in the array) now contains a pointer to memory that has been returned to the system.

The compiler generated copy constructor; copies each member variable by using that members copy constructor. For pointers this just means the pointer value is copied from the source object to the destination object (hence shallow copy).

The compiler generated assignment operator; copies each member variable by using that members assignment operator. For pointers this just means the pointer value is copied from the source object to the destination object (hence shallow copy).

So the minimum for a class that contains a pointer:

class A
{
    size_t     mSize;
    int*       mArray;
    public:
         // Simple constructor/destructor are obvious.
         A(size_t s = 0) {mSize=s;mArray = new int[mSize];}
        ~A()             {delete [] mArray;}

         // Copy constructor needs more work
         A(A const& copy)
         {
             mSize  = copy.mSize;
             mArray = new int[copy.mSize];

             // Don't need to worry about copying integers.
             // But if the object has a copy constructor then
             // it would also need to worry about throws from the copy constructor.
             std::copy(&copy.mArray[0],&copy.mArray[c.mSize],mArray);

         }

         // Define assignment operator in terms of the copy constructor
         // Modified: There is a slight twist to the copy swap idiom, that you can
         //           Remove the manual copy made by passing the rhs by value thus
         //           providing an implicit copy generated by the compiler.
         A& operator=(A rhs) // Pass by value (thus generating a copy)
         {
             rhs.swap(*this); // Now swap data with the copy.
                              // The rhs parameter will delete the array when it
                              // goes out of scope at the end of the function
             return *this;
         }
         void swap(A& s) noexcept
         {
             using std::swap;
             swap(this.mArray,s.mArray);
             swap(this.mSize ,s.mSize);
         }

         // C++11
         A(A&& src) noexcept
             : mSize(0)
             , mArray(NULL)
         {
             src.swap(*this);
         }
         A& operator=(A&& src) noexcept
         {
             src.swap(*this);     // You are moving the state of the src object
                                  // into this one. The state of the src object
                                  // after the move must be valid but indeterminate.
                                  //
                                  // The easiest way to do this is to swap the states
                                  // states of the two objects.
                                  //
                                  // Note: Doing any operation on src after a move 
                                  // is risky (apart from destroy) until you put it 
                                  // into a specific state. Your object shoudl have
                                  // appropriate methods for this.
                                  // 
                                  // Example: Assignment (operator = should work).
                                  //          std::vector() has clear() which sets
                                  //          a specific state without needing to
                                  //          know the current state.
             return *this;
         }   
 }

这篇关于动态分配对象数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆