我熟悉C ++,而且我遇到了内存管理方面的问题。在C中,每当我想为任意数量的元素保留内存时,无论何种类型,我都会调用malloc()
,然后手动初始化(通过循环),以我想要的任何值。使用C ++的new
,一切都是自动初始化的。
问题是,我有一个BattlePoint类,它有点像这样:
class BattlePoint {
public:
BattlePoint(int x, int y) : x(x), y(y) { };
bool operator==(const BattlePoint &right);
virtual ~BattlePoint();
private:
int x, y;
};
正如您所看到的,它通过初始化器获取了一些x和y值,然后从中设置了自己的x和y。问题是,这个函数将从一个函数中调用,该函数将分配它们的数组:
BattleShip::BattleShip(BattlePoint start, enum shipTypeSize size, enum shipOrientation orientation) : size(size), orientation(orientation) {
points = new BattlePoint[size]; // Here be doubts.
}
所以,我需要我的BattleShip指向一个BattlePoints数组,每个BattlePoints都有不同的初始值(例如0,1; 0,2; 0,3等)。
问题是:我如何分配未初始化的内存?
儒略,
P.S。:我没有对new
的工作方式进行过任何测试,我简单阅读Wikipedia's article on it说:
在C ++编程语言中,以及在许多基于C ++的编程语言中 语言,new是一种动态分配的语言结构 堆上的内存和使用。初始化内存 构造即可。除了名为“placement new”的表单,新的 尝试在堆上为新数据分配足够的内存。如果 成功后,它会初始化内存并将地址返回给 新分配和初始化的内存。但是如果新的不能分配 堆上的内存会抛出std :: bad_alloc类型的异常。 这消除了显式检查分配结果的需要。 调用delete,调用析构函数并返回内存 由new返回堆分配,必须为每次调用new 避免内存泄漏。
placement new应该是解决方案,但它没有提及如何做到这一点。
P.S。 2:我知道这可以通过stdlib的vector类来完成,但我是故意避免的。
答案 0 :(得分:7)
您需要使用std::vector
。在这种情况下,您可以push_back
随意使用,例如
std::vector<BattlePoint> x;
x.push_back(BattlePoint(1, 2));
如果曾发现自己使用new[]
,delete
或delete[]
,请立即重构您的程序以删除此类程序。几乎在任何可想象的方式中,它们都是非常不安全的。相反,请使用资源管理类,例如std::unique_ptr
,std::vector
和std::shared_ptr
。
常规new
可以在某些涉及unique_ptr
的情况下有用,但请避免使用它。此外,新位置通常不值得。当然,如果您正在编写资源管理类,那么您可能不得不将它们用作基础原语,但这只是很少而且很远。
编辑:我的错误,我没有看到你问题的最后一行。解决它:
P.S。 2:我知道这可以通过stdlib的vector类来完成,但我是 故意避免它。
如果您有针对标准库的广告系列,请滚动您自己的vector
替换。但是不要没有vector
课程。有一个原因是必须由所有符合要求的编译器提供。
答案 1 :(得分:4)
points = new BattlePoint [size]; //这里有疑惑。
附: 2:我知道这可以通过stdlib的vector类来完成,但我是故意避免的。
肯定会有疑惑!使用std::vector
。你为什么不呢? 没有理由不使用std::vector
,尤其是,如果它可以解决您的问题。
std::vector<BattlePoint> bpoints;
bpoints.reserve(size); // there, only alloc'd memory, not initialized it.
bpoints.push_back(some_point); // still need to use push_back to initialize it
我确定问题会出现 - std::vector
如何只分配内存?!
operator new
就是答案。当您使用new
时,它是调用内存分配的运算符。 new
用于构造和初始化,而operator new
用于分配(这就是为什么你可以重载它)。
BattlePoint* bpoints = ::operator new(size); // happens in reserve
new (bpoints[index]) BattlePoint(some_x, some_y); // happens in push_back
答案 2 :(得分:3)
为了回应上述答案,我肯定会指向std::vector
,因为这是最好的解决方案。在C++
中管理自己的动态数组几乎从来都不是一个好主意,几乎从来没有必要。
然而,要回答直接问题 - 在这种情况下,你可以创建一个默认构造函数和一些mutator来获得所需的效果:
class BattlePoint {
public:
// default constructor, default initialize to 0,0
BattlePoint() x(0), y(0) {};
BattlePoint(int x, int y) : x(x), y(y) { };
bool operator==(const BattlePoint &right);
virtual ~BattlePoint();
// mutator functions allow you to modify the classes member values
void set_x(int x_) {x = x_;}
void set_y(int y_) {y = y_;}
private:
int x, y;
};
然后您可以按照C
中的惯例初始化它:
BattlePoint* points = new BattlePoint[100];
for(int x = 0; x < 100; ++x)
{
points->set_x(x);
points->set_y(x * 2);
}
如果你对基本上使BattlePoint
类公开变异感到困扰,你可以保持mutator私有并引入一个专门用于初始化值的友元函数。这是一个稍微复杂一点的概念,所以我现在暂时不做解释,除非需要。
既然你问过:)
使用默认构造函数和mutators再次创建BattlePoint
类,但这次将mutator保持为private,并声明一个友元函数来使用它们:
class BattlePoint {
public:
// default constructor, default initialize to 0,0
BattlePoint() x(0), y(0) {};
BattlePoint(int x, int y) : x(x), y(y) { };
bool operator==(const BattlePoint &right);
virtual ~BattlePoint();
private:
// mutator functions allow you to modify the classes member values
void set_x(int x_) {x = x_;}
void set_y(int y_) {y = y_;}
int x, y;
friend void do_initialize_x_y(BattlePoint*, int, int);
};
创建一个头文件,其中包含用于创建BattlePoint
个对象数组的本地函数。任何包含标题的人都可以使用此功能,但如果命名正确,那么“所有人”应该知道不使用它。
// BattlePoint_Initialize.h
BattlePoint* create_battle_point_array(size_t count, int* x, int* y);
此功能在实现文件中定义,以及我们将“隐藏”外部世界的朋友功能:
// BattlePoint_Initialize.cpp
#include <BattlePoint_Initialize.h>
namespace
{
// by putting this function in an anonymous namespace it is only available
// to this compilation unit. This function can only be called from within
// this particular file.
//
// technically, the symbols are still exported, but they are mangled badly
// so someone could call this, but they would have to really try to do it
// not something that could be done "by accident"
void do_initialize_x_y(BattlePoint* bp, int x, int y)
{
bp->set_x(x);
bp->set_y(y);
}
}
// caution, relies on the assumption that count indicates the number of
// BattlePoint objects to be created, as well as the number of valid entries
// in the x and y arrays
BattlePoint* create_battle_point_array(size_t count, int* x, int* y)
{
BattlePoint* bp_array = new BattlePoint[count];
for(size_t curr = 0; curr < count; ++curr)
{
do_initialize_x_y(bp_array[curr], x[curr], y[curr]);
}
return bp_array;
}
所以你有它。一种非常复杂的方式来满足您的基本要求。
虽然理论上可以在任何地方调用create_battlepoint_array()
,但它实际上无法修改已创建的BattlePoint
对象。隐藏在初始化代码后面的匿名do_initialize_x_y()
隐藏的namespace
函数无法从程序中的任何其他位置轻松调用。实际上,一旦创建了BattlePoint
对象(并在两个步骤中初始化),就无法进一步修改它。
答案 3 :(得分:2)
comp.lang.c++ FAQ在这个问题上有用处,包括试图阻止你使用新的位置 - 但是如果你真的坚持,它确实有一个关于placement new的有用部分及其所有陷阱