分配内存而不在C ++中初始化它

时间:2011-12-26 00:47:07

标签: c++ memory new-operator

我熟悉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类来完成,但我是故意避免的。

4 个答案:

答案 0 :(得分:7)

您需要使用std::vector。在这种情况下,您可以push_back随意使用,例如

std::vector<BattlePoint> x;
x.push_back(BattlePoint(1, 2));

如果发现自己使用new[]deletedelete[],请立即重构您的程序以删除此类程序。几乎在任何可想象的方式中,它们都是非常不安全的。相反,请使用资源管理类,例如std::unique_ptrstd::vectorstd::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的有用部分及其所有陷阱