std::string 会删除通过 new 分配的 char 缓冲区吗?

发布于 01-15 02:54 字数 612 浏览 4 评论 0原文

这里是否存在内存泄漏?:

#include <iostream>
#include <string>

class A{
    std::string value;
public:
    A(){
        char *tmp = new char[3];
        tmp[0] = 'a';
        tmp[1] = 'b';
        tmp[2] = 'c';
        value = std::string(tmp);
    }
    std::string const& getValue() const {
        return value;
    }
};

int main(){
    A a;
    std::cout << a.getValue() << '\n';
}

由于该值是从指向newed内存的指针构造的,因此std::string正在获取它的所有权,它也会破坏?还是我有责任照顾那个临时物品?如果是这样,销毁它的唯一方法是将其作为私有变量,否则它将超出范围(因为指针是在构造函数中创建的自动存储)。

那么这里的缓冲区是如何管理的呢?

Is here a memory leak?:

#include <iostream>
#include <string>

class A{
    std::string value;
public:
    A(){
        char *tmp = new char[3];
        tmp[0] = 'a';
        tmp[1] = 'b';
        tmp[2] = 'c';
        value = std::string(tmp);
    }
    std::string const& getValue() const {
        return value;
    }
};

int main(){
    A a;
    std::cout << a.getValue() << '\n';
}

since the value is constructed from pointer to newed memory, and thus the std::string is taking the ownership of it, will it also destroy? Or is it my responsibility to take care of that temporary object? And if so, the only way to destroying it is via having it as private variable, otherwise it goes out of scope (since the pointer is with auto storage created in constructor function).

So how is here the buffer managed?

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

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

发布评论

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

评论(5

寄离2025-01-22 02:54:30

因此 std::string 取得了它的所有权

这个假设是不正确的。 std::string 不获取任何指针的所有权,构造函数采用 char* 将数据复制到内部存储中。所以,是的,您必须delete[]new[]'ed的内容。


另外,如果您将 char* 传递给 std::string,它必须以 null 结尾:

    A(){
        char *tmp = new char[4];
        tmp[0] = 'a';
        tmp[1] = 'b';
        tmp[2] = 'c';
        tmp[3] = '\0';
        value = std::string(tmp);
    }

thus the std::string is taking the ownership of it

This assumption is incorrect. std::string doesn't take ownership of any pointer, constructor taking char* copies the data into internal storage. So yes, you have to delete[] what you new[]'ed.


Also, if you are passing char* to std::string, it must be null terminated:

    A(){
        char *tmp = new char[4];
        tmp[0] = 'a';
        tmp[1] = 'b';
        tmp[2] = 'c';
        tmp[3] = '\0';
        value = std::string(tmp);
    }
书间行客2025-01-22 02:54:30

答案是否定的。我的意思是想象一下,如果它做到了,并且您这样做了(忽略缺少的 0),

    char *tmp = new char[3];
    tmp[0] = 'a';
    tmp[1] = 'b';
    tmp[2] = 'c';
    std::string value1 = std::string(tmp);
    std::string value2 = std::string(tmp);

如果第二个字符串创建失败,您会非常沮丧

而且 std::string(const char*s) 无法判断内存for s 位于堆、堆栈或静态上。它所能看到的只是一个指针。即使它想窃取记忆,它也无法知道它是否可行

The answer is no. I mean imagine if it did and you did this (ignoring the missing 0)

    char *tmp = new char[3];
    tmp[0] = 'a';
    tmp[1] = 'b';
    tmp[2] = 'c';
    std::string value1 = std::string(tmp);
    std::string value2 = std::string(tmp);

You would be very upset if the second string creation failed

Also std::string(const char*s) cannot tell that the memory for s is on the heap, stack or static. All it can see is a pointer. Even if it wanted to steal the memory it has no way of knowing if its doable

蓝眼睛不忧郁2025-01-22 02:54:30

因此 std::string 取得了它的所有权,它也会销毁吗?

如果您拥有资源的所有权,则意味着您最终将释放该资源(除非您将其转让给其他所有者,并且除非所有权是共享的)。如果您不释放资源(或适用上述例外情况),则要么您没有获得所有权,要么您有错误。

std::string 只会删除 std::string 创建的缓冲区。 std::string 不会取得您创建的缓冲区的所有权。您对“std::string 正在取得所有权”的假设是错误的。

永远不要尝试猜测某些函数/构造函数是否拥有所有权。始终依靠文档来找出答案。

或者我有责任照顾那个临时物品吗?

是的。您负责释放您创建的分配(直到您将所有权转移到智能指针之类的东西)。

您创建的动态分配会泄漏,因为您没有释放它。

如果是这样,销毁它的唯一方法是将其作为私有变量,否则它就会超出范围

您可以在创建 std::string 后简单地删除它,因此您的缓冲区是不再使用。

重要! 接受 char*std::string 构造函数要求指向的字符串以 null 结尾。您的字符串不包含空终止符,因此调用违反了前提条件。您的程序的行为是未定义的。

而且,分配是完全没有必要的。您可以通过编写如下所示的构造函数来实现预期的行为并修复未定义的行为和内存泄漏:

A(): value("abc") {}

但是如果我从另一个分配它的函数获取 char 指针怎么办

主要是,尽量避免调用此类函数。

但如果您没有其他选择,那么您必须了解该函数正在将分配的所有权转移给您(我想不出该函数会记录它使用 std::malloc 的任何其他原因) >)。一旦不再需要它,您必须使用 std::free 释放它。理想情况下,您应该使用智能指针之类的东西来执行此操作。

我使用了 uuid_unparse ,它给出了从 malloc 生成的 char 指针(请参阅其文档)

根据

uuid_t some_uuid = example_uuid();
constexpr std::size_t uuid_size = 36;
std::string str(uuid_size, '\0');
uuid_unparse(some_uuid, str.data());

And thus the std::string is taking the ownership of it, will it also destroy?

If you take ownership of a resource, that means that you will eventually release the resource (unless you transfer it to some other owner, and unless the ownership is shared). If you don't release the resource (or the mentioned exceptions apply), then either you have not taken ownership, or you have a bug.

std::string will only delete a buffer that std::string has created. std::string will not take ownership of a buffer that you have created. Your assumption of "std::string is taking the ownership" is wrong.

Never try to guess whether some function / constructor takes ownership or not. Always rely on documentation to find that out.

Or is it my responsibility to take care of that temporary object?

Yes. You are responsible for freeing the allocations that you create (until you transfer the onwership to something like a smart pointer).

The dynamic allocation that you created leaks, because you don't deallocate it.

And if so, the only way to destroying it is via having it as private variable, otherwise it goes out of scope

You can simply delete it after the std::string has been created and thus your buffer is no longer used.

Important! std::string constructor that accepts char* requires that the pointed string is null terminated. Your string doesn't contain the null terminator character, and thus the call violates the precondition. The behaviour of your program is undefined.

Furthermore, the allocation is entirely unnecessary. You can achieve the intended behaviour and fix both the undefined behaviour and the memory leak by writing the constructor like this:

A(): value("abc") {}

but what if I get the char pointer from another function that malloced it

Primarily, try to avoid calling such functions.

But in case you have no other option, then you must understand that the function is transferring the ownership of the allocation to you (I cannot think of any other reason the function would document that it used std::malloc). You must deallocate it using std::free once you no longer need it. Ideally, you should use something like a smart pointer to do so.

I used uuid_unparse which gives char pointer generated from malloc (see its documentation)

According to the documentation of uuid_unparse, it doesn't do such thing. It doesn't allocate anything. If you want to use it with std::string, you can do this:

uuid_t some_uuid = example_uuid();
constexpr std::size_t uuid_size = 36;
std::string str(uuid_size, '\0');
uuid_unparse(some_uuid, str.data());
夜巴黎2025-01-22 02:54:30

文档中解释的第五个构造函数采用 char* 参数。这意味着它获取指向 c 风格字符串的指针,计算其长度,然后转换为 std::string。原始 c 风格 char* 字符串的内存不会被释放,因此除非您稍后在某处删除 char* 字符串,否则会出现内存泄漏在代码中。

The 5th constructor explained in the documentation takes the char* argument. It means it takes the pointer pointing the c-style string, calculates its length and then converts to std::string. The memory of original c-style char* string isn't released, so you have memory leak unless you later delete the char* string somewhere in code.

淡水深流2025-01-22 02:54:30

最有趣的评论:

@MarekR 重点是,我尝试复制我得到的错误。我用过
uuid_unparse 它给出了从 malloc 生成的 char 指针(请参阅其
文档),但在处理 uuid 的类中我有 std::string。和
由于 std::string 没有所有权,我必须更改
仅处理 char 指针的类的实现 -
米兰赫拉博斯

显然,您正在以错误的方式使用 uuid_unparse 与 c++ ,当问题出现时,您提出了问题不反映实际问题。

我会按以下方式使用这个API:

std::string uuid_to_string(uuid_t uu)
{
    char buff[37];
    uuid_unparse(uu, buff);
    return buff; // implicit conversion to std::string is done here.
}

或更奇特的方式来做到这一点:

std::string uuid_to_string(uuid_t uu)
{
    std::string buff(size_t{36}, '\0');
    uuid_unparse(uu, buff.data()); // since C++17 data returns `char *`
    return buff;
}

About your code:

        char *tmp = new char[3];
        tmp[0] = 'a';
        tmp[1] = 'b';
        tmp[2] = 'c';
        value = std::string(tmp);

这有3个问题:

  1. 您正在分配未释放的内存(字符串不接管它的所有权) - 所以内存泄漏
  2. 您的缓冲区不包含以零结尾,因此当它传递给 std::string 时,这会导致未定义的行为,因为无法确定字符串的
  3. 代码过于复杂。在了解其目的是什么之后,我能够提供简单的解决方案。

Most interesting comment:

@MarekR the point was, I tried to replicate the error I got. I used
uuid_unparse which gives char pointer generated from malloc (see its
documentation), but in class handling uuid I had std::string. And
since the std::string does not have ownership, I had to change the
implementation of the class to handle char pointers only –
milanHrabos

Apparently you are using uuid_unparse with c++ in wrong way and when problem appeared you asked question which do not reflect actual problem.

I would use this API in following way:

std::string uuid_to_string(uuid_t uu)
{
    char buff[37];
    uuid_unparse(uu, buff);
    return buff; // implicit conversion to std::string is done here.
}

or more fancy way to do it:

std::string uuid_to_string(uuid_t uu)
{
    std::string buff(size_t{36}, '\0');
    uuid_unparse(uu, buff.data()); // since C++17 data returns `char *`
    return buff;
}

About your code:

        char *tmp = new char[3];
        tmp[0] = 'a';
        tmp[1] = 'b';
        tmp[2] = 'c';
        value = std::string(tmp);

this has 3 issues:

  1. you are allocating memory which is not freed (string doesn't take over ownership of it) - so memory leak
  2. your buffer do not contain terminating zero, so when it is passed to std::string this leads to undefined behavior since and of string can't be determined
  3. code is simply over-complicated. After understudying what was its purpose I was able to provide simple solution.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文