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

发布于 2024-12-23 00:20:30 字数 1376 浏览 0 评论 0 原文

我正在熟悉 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.
}

所以,我需要我的战舰点来保存一组 BattlePoints,每个 BattlePoints 都有不同的初始化值(例如 0,1; 0,2 ;0,3,等等)。

问题是:如何分配未初始化的内存?

Julian,

PS:我还没有对 new 的工作方式进行任何测试,我简单地阅读了 维基百科关于它的文章 内容如下:

在 C++ 编程语言以及许多基于 C++ 的语言中 语言中,new 是一种动态分配的语言结构 堆上的内存并使用初始化内存 构造函数。除了一种称为“安置新”的形式外,新 尝试在堆上为新数据分配足够的内存。如果 成功,初始化内存并返回地址 新分配和初始化的内存。但是如果 new 无法分配 堆上的内存会抛出 std::bad_alloc 类型的异常。 这消除了显式检查分配结果的需要。 调用delete,它调用析构函数并返回内存 由 new 分配回堆,每次调用 new 时都必须进行 以避免内存泄漏。

放置 new 应该是解决方案,但它没有提及如何做到这一点。

PS 2:我知道这可以通过 stdlib 的向量类来完成,但我故意避免它。

I'm getting acquainted with C++, and I'm having a problem with memory management. In C, whenever I'd want to reserve memory for any number of elements, regardless of type, I would just call malloc() and then initialize by hand (through a loop), to whichever value I wanted. With C++'s new, everything is automagically initialized.

Problem is, I've got a BattlePoint class which goes a little something like this:

class BattlePoint {
public:
    BattlePoint(int x, int y) : x(x), y(y) { };
    bool operator==(const BattlePoint &right);
    virtual ~BattlePoint();

private:
    int x, y;
};

As you can see, it takes a few x and y values through the initializer and then sets its own x and y from it. The problem is, this function will be called from a function which will allocate an array of them:

BattleShip::BattleShip(BattlePoint start, enum shipTypeSize size, enum shipOrientation orientation) : size(size), orientation(orientation) {
    points = new BattlePoint[size]; // Here be doubts.
}

So, I need my BattleShip's point to hold an array of BattlePoints, each one with different initialization values (such as 0,1; 0,2; 0,3, etcetera).

Question is: how could I allocate my memory uninitialized?

Julian,

P.S.: I haven't done any testing regarding the way new works, I simple read Wikipedia's article on it which says:

In the C++ programming language, as well as in many C++-based
languages, new is a language construct that dynamically allocates
memory on the heap and initialises the memory using the
constructor
. Except for a form called the "placement new", new
attempts to allocate enough memory on the heap for the new data. If
successful, it initialises the memory and returns the address to the
newly allocated and initialised memory. However if new cannot allocate
memory on the heap it will throw an exception of type std::bad_alloc.
This removes the need to explicitly check the result of an allocation.
A call to delete, which calls the destructor and returns the memory
allocated by new back to the heap, must be made for every call to new
to avoid a memory leak.

placement new should be the solution, yet it makes no mention on how to do it.

P.S. 2: I know this can be done through stdlib's vector class, but I'm avoiding it on purpose.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

递刀给你 2024-12-30 00:20:30

您需要使用std::vector。在这种情况下,您可以push_back任何您想要的内容,例如

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

,如果您曾经发现自己使用new[]delete ,或delete[],立即重构您的程序以删除此类内容。他们几乎在所有可以想象到的方面都非常不安全。相反,请使用资源管理类,例如 std::unique_ptrstd::vectorstd::shared_ptr

常规new可能在涉及unique_ptr的某些情况下很有用,但要避免使用它。此外,放置新的通常不值得。当然,如果您正在编写一个资源管理类,那么您可能必须将它们用作底层原语,但这种情况很少而且相差很远。

编辑:我的错误,我没有看到你问题的最后一行。解决它:

PS 2:我知道这可以通过 stdlib 的向量类来完成,但我
故意避免它。

如果您有一些针对标准库的活动,请推出您自己的向量替代品。但不要没有 vector 类。所有符合标准的编译器都必须提供它,这是有原因的。

You need to use a std::vector. In this case you can push_back whatever you want, e.g.

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

If you ever find yourself using new[], delete, or delete[], refactor your program immediately to remove such. They are hideously unsafe in virtually every way imaginable. Instead, use resource-managing classes, such as std::unique_ptr, std::vector, and std::shared_ptr.

Regular new can be useful in some situations involving unique_ptr, but else avoid it. In addition, placement new is usually not worth it. Of course, if you're writing a resource-managing class, then you may have to use them as underlying primitives, but that's few and very far between.

Edit: My mistake, I didn't see the very last line of your question. Addressing it:

P.S. 2: I know this can be done through stdlib's vector class, but I'm
avoiding it on purpose.

If you have some campaign against the Standard Library, then roll your own vector replacement. But do not go without a vector class. There's a reason that it must be provided by all conforming compilers.

安静被遗忘 2024-12-30 00:20:30

点数 = new BattlePoint[大小]; // 这里有疑问。
PS 2:我知道这可以通过 stdlib 的向量类来完成,但我故意避免它。

肯定会有疑问!使用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

points = new BattlePoint[size]; // Here be doubts.
P.S. 2: I know this can be done through stdlib's vector class, but I'm avoiding it on purpose.

Most certainly there will be doubts! Use std::vector. Why wouldn't you? There is no reason not to use std::vector, especially if it solves your problem.

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

I'm sure the question will come - how does std::vector only alloc the memory?!
operator new is the answer. It's the operator that gets called for memory allocation when you use new. new is for construction and initialization, while operator new is for allocation (that's why you can overload it).

BattlePoint* bpoints = ::operator new(size); // happens in reserve
new (bpoints[index]) BattlePoint(some_x, some_y); // happens in push_back
愿与i 2024-12-30 00:20:30

comp.lang.c++ 常见问题解答提供了有用的信息就此事发表意见,包括试图劝阻您不要使用新的展示位置 - 但如果您真的坚持,它确实有一个关于新的展示位置及其所有陷阱。

The comp.lang.c++ FAQ has useful things to say on the matter, including attempting to dissuade you from using placement new - but if you really insist, it does have a useful section on placement new and all its pitfalls.

小女人ら 2024-12-30 00:20:30

为了呼应上述答案,我肯定会向您指出 std::vector ,因为它是最好的解决方案。在 C++ 中管理自己的动态数组几乎从来都不是一个好主意,也几乎从来没有必要。

但是,要回答直接问题 - 在这种情况下,您可以创建一个默认构造函数和一些修改器以获得所需的效果:

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 类公开可变感到烦恼,您可以将变异器保持为私有并引入一个专门用于初始化值的友元函数。这是一个稍微复杂一些的概念,因此除非需要,否则我现在将放弃对此进行进一步解释。

既然您问了:)

使用默认构造函数和变异器再次创建您的 BattlePoint 类,但是这次将变异器保留为私有,并声明一个友元函数来使用它们:

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() 函数本质上隐藏在初始化代码后面的匿名命名空间 中,无法轻松地从程序中的其他任何地方调用。实际上,一旦创建了 BattlePoint 对象(并分两步初始化),就无法进一步修改。

To echo the above answers, I would most certainly point you towards std::vector as it is the best possible solution. Managing your own dynamic arrays in C++ is almost never a good idea, and is almost never necessary.

However, to answer the direct question -- in this situation you can create a default constructor and some mutators to get the desired effect:

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;
};

Then you can initialize this as you are used to in C:

BattlePoint* points = new BattlePoint[100];

for(int x = 0; x < 100; ++x)
{
   points->set_x(x);
   points->set_y(x * 2);
}

If you're bothered by basically making the BattlePoint class publically mutable, you can keep the mutators private and introduce a friend function specifically for initializing the values. This is a slightly more involved concept, so I'll forgo further explanation on this for now, unless it is needed.

Since you asked :)

Create your BattlePoint class again with a default constructor and mutators, however this time leave the mutators private, and declare a friend function to use them:

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);
};

Create a header file that will contain a local function for creating the array of BattlePoint objects. This function will be available to anyone that includes the header, but if named properly then "everyone" should know not to use it.

// BattlePoint_Initialize.h
BattlePoint* create_battle_point_array(size_t count, int* x, int* y);

This function gets defined in the implementation file, along with our friend function that we will "hide" from the outside world:

// 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;
}

So there you have it. A very convoluted way to meet your basic requirements.

While, create_battlepoint_array() could in theory be called anywhere, it's actually not capable of modifying an already created BattlePoint object. The do_initialize_x_y() function by nature of being hidden in an anonymous namespace tucked away behind the initialization code cannot easily be called from anywhere else in your program. In effect, once a BattlePoint object has been created (and initialized in two steps), it cannot be modified further.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文