动态分配一系列的对象
-
05-07-2019 - |
题
这是个初学者的问题,但是我没有做C++在很长一段时间,所以这里去...
我有一个类,包含一个动态分配阵列,说
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;
}
}
但现在我想创建一个动态分配的阵列的这些课程。这是我的前代码:
A* arrayOfAs = new A[5];
for (int i = 0; i < 5; ++i)
{
arrayOfAs[i] = A(3);
}
但是,这种打击了可怕的。因为新的 A
对象创造的(与 A(3)
呼叫)被破坏时的 for
回路迭代结束,这意味着内部 myArray
那 A
实例得到 delete []
-ed.
因此,我认为我的语法必须是可怕的错误?我想有一些修正,似乎喜欢矫枉过正,我希望避免:
- 创建一个副本构造
A
. - 使用
vector<int>
和vector<A>
所以我不必担心这一切。 - 而不是有
arrayOfAs
被一阵的A
对象,它是一系列A*
指针。
我想这只是一些初学者的事情那里的语法,实际工作当尝试的动态分配一系列事情有内部动态的分配。
(此外,风格批评以理解的,因为它已经有一段时间,因为我有没有C++.)
更新用于未来的观众:所有的答案下真的有帮助的。马丁是被接受,因为示例代码和有用"的规则4,"但是我真的建议阅读他们所有。有些是好的,简明扼要的发言的有什么错误,一些正确地指出如何和为什么 vector
s是一个良好的路要走。
解决方案
对于建设集装箱你很明显想要使用一个标准的容器(例如性传染病::矢量)。但是,这是一个完美的例子的事情你需要时要考虑的对象包含原始指针。
如果你的对象都具有一个原始指针然后你必须记住规则3(现在的规则5C++11条)。
- 构造
- 析构
- Copy Constructor
- 分配运营商
- 移动的构造(C++11)
- 移动分配(C++11)
这是因为如果没有定义的编译器将会产生其自己的版本的这些方法(见下文)。编译器而产生的版本并不总是有用的当处理原指针。
复制的构造是很难获得正确的(这是不平凡的如果你想要提供强大的例外,保证).分配运营商可以定义条款的复制构造作为可以使用复制和交换句成语国内。
见下文的详情在绝对最小一类含一个指向一系列的整数。
我们知道,这是不容易得到这正确的,你应该考虑使用标准::矢量,而不是一个指向一系列的整数。矢量是易于使用(和扩大)和涵盖所有相关的问题与例外情况。比较以下类的定义如下。
class 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)
{
// 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.
}
编译器而产生的分配运营者对于几乎所有情况,但是当原始指针是在玩你需要注意。在你的情况这是造成问题因为 浅的副本 问题。你已经结束了两种对象包含指向同一块记忆。当A(3)超出范围的循环结束它要求删除[]在其指针。因而其他的对象(在阵列)现在包含了一个指向记忆那已经返回到系统中。
编译器生成的副本构造;副本的每个成员变通使用,成员国的副本构造。为指针,这就意味着针的价值是从来源复制目的向目的目的(因此浅的副本)。
编译器而产生的分配运营商;副本的每个成员变通使用,成员分配运营商。为指针,这就意味着针的价值是从来源复制目的向目的目的(因此浅的副本)。
所以最小的一类,它包含一个指针:
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(©.mArray[0],©.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
// 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 should 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;
}
}
其他提示
我建议使用std::矢量:喜欢的东西
typedef std::vector<int> A;
typedef std::vector<A> AS;
没有什么是错误的,与轻微的矫枉过正的STL,你将能够花更多的时间实现的具体特征的应用程序,而不是重新塑造自行车。
构造你的一个对象分配了另一个对象动态和商店指针动态,分配的对象在一个原指针。
对于这种情况下,您 必须 定义自己的构造复制、分配运营商和destructor.编译器而产生的人将不能正常工作。(这是一个必然结果"的法律的三大":一类的任何析构,分配员,复制构造一般需要3).
你定义自己析构(和你提到创建一个复制构造的),但是你需要定义的其他2个大的三个。
一种替代方法是储存的指针到你的动态分配 int[]
在其他一些对象,将照顾这些东西给你。有点像 vector<int>
(正如你所提到)或一个 boost::shared_array<>
.
把这下利用RAII充分的程度,应该避免处理原始指向可能的程度。
而且,由于你要求对其他的风格批评,未成年人之一是,当你是删除原指针,你不需要检查对0话之前 delete
- delete
处理这种情况下这样做没有什么所以你不要弄乱你的代码的检查。
使用列或共同的容器为对象的,只有当他们有默认和复制的构造.
存储指针,否则(或智能指针,但是可以满足一些问题,在这种情况下)。
PS:总是定义自己的默认和复制的构造,否则自动生成的将使用
你需要一个分配运营商,以便:
arrayOfAs[i] = A(3);
作为这是应该的。
为什么没有一个setSize方法。
A* arrayOfAs = new A[5];
for (int i = 0; i < 5; ++i)
{
arrayOfAs[i].SetSize(3);
}
我喜欢"副本",但在这种情况下默认的构造是不是真的在做什么。SetSize可以复制的数据的原始m_array(如果存在)..你得储存的大小的数组类内做到这一点。
或
SetSize可以删除原始m_array.
void SetSize(unsigned int p_newSize)
{
//I don't care if it's null because delete is smart enough to deal with that.
delete myArray;
myArray = new int[p_newSize];
ASSERT(myArray);
}
使用的放置位置的特征 new
操作员,您可以创建的目的在地方和避免复制:
安置(3):void*运营者的新的(std::位置的尺寸,void*ptr)noexcept;
简单地返回ptr(没有储存是分配).注意到,虽然说,如果功能是由一个新的表达,适当的初始化将执行(类对象,这包括调用其default constructor).
我建议如下:
A* arrayOfAs = new A[5]; //Allocate a block of memory for 5 objects
for (int i = 0; i < 5; ++i)
{
//Do not allocate memory,
//initialize an object in memory address provided by the pointer
new (&arrayOfAs[i]) A(3);
}