【问题标题】:String Allocation In C++C++中的字符串分配
【发布时间】:2013-07-23 12:34:07
【问题描述】:

当我发现一些奇怪的东西时,我正在使用重载的 new 和 delete 运算符。

  1. 我有:

    void* operator new(size_t size)
    {
        std::cout << "Allocating memory..." << std::endl;
    
        void* p = malloc(size);
    
        if (NULL == p)
        {
            throw std::bad_alloc();
        }
    
        return p;
    }
    
  2. 当我这样做时:

    int main()
    {
        int* x = new int(1);
        std::cout << *x << std::endl;
        delete x;
        return EXIT_SUCCESS;
    }
    

    一切都按预期进行,我得到:

    Allocating memory...
    1
    
  3. 但是当我这样做时:

    int main()
    {
        std::string* s = new std::string("Hello world");
        std::cout << *s << std::endl;
        delete s;
        return EXIT_SUCCESS;
    }
    

    我明白了:

    Allocating memory...
    Allocating memory...
    Hello world
    
  4. 事实上,当我这样做时:

    int main()
    {
        std::string s = "Hello world";
        return EXIT_SUCCESS;
    }
    

    我仍然得到Allocating memory...

  5. 最后,我做到了:

    int main()
    {
        std::string s = "Hello world";
        std::cout << &s << std::endl;
        while (true);
    }
    

    得到类似的东西:

    $ ./test &
    [1] 8979
    Allocating memory...
    0xbfc39a68
    $ cat /proc/8979/maps | grep stack
    bfc27000-bfc3c000 ... [stack]
    

    所以现在我确定s 变量是在堆栈上分配的......但是,什么叫new 运算符?我最好的猜测是它与实际文字的内存分配有关,"Hello world"... 但它应该是静态内存,new 是关于动态内存的。

发生了什么事?

更新

在阅读了 cmets 并亲自调试了示例之后,我想得出结论,确实,一旦调用了字符串构造函数,它就会在堆上为其内部实现分配内存。这可以通过跟踪new 调用来查看:

(gdb) b 13 // that's the std::cout << "Allocating memory..." << std::endl; line
(gdb) r
... Breakpoing 1, operator new (size=16) at test.cpp:13 ...
(gdb) backtrace
#0 operator new (size=16) at main.cpp:13
#1 std::string::_Rep::_S_create(unsigned int, unsigned int, std::allocator<char> const&) () from /usr/lib/libstdc++.so.6
...

并阅读 std::string(好吧,basic_string.tcc)源代码:

template<typename _CharT, typename _Traits, typename _Alloc>
typename basic_string<_CharT, _Traits, _Alloc>::_Rep*
basic_string<_CharT, _Traits, _Alloc>::_Rep::
_S_create(size_type __capacity, size_type __old_capacity,
          const _Alloc& __alloc)
{

   ...

    void* __place = _Raw_bytes_alloc(__alloc).allocate(__size);
   _Rep *__p = new (__place) _Rep; // Bingo!
   __p->_M_capacity = __capacity;

   ...

}

是的。编程很酷。

【问题讨论】:

  • 注意:malloc/freenew/delete - 是一对
  • 你为什么不使用调试器并打破你的operator new 看看是谁在调用它?流也会分配内存。
  • std::string 的全部意义在于它封装了一个char* 并为您拥有/管理它。因此,当您创建std::string 时,它会分配一些内存来存储实际字符。构造函数甚至可以采用自定义分配器。
  • @GrijeshChauhan:我还通过free 实现了delete,如果这就是你的意思,但它与问题无关,所以我省略了。
  • 不会是代码 3 中的std::string *s 吗?

标签: c++ string memory


【解决方案1】:

当你写作时

std::string s = "Hello world";

你在调用构造函数string (const char* s);,这个构造函数的规范是Copies the null-terminated character sequence (C-string) pointed by s.所以构造函数分配内存来存储副本。

【讨论】:

  • 最后一句中的比较混乱,最好换成所以s需要分配内存来存放副本但是那么我建议您更改构造函数参数名称,因为它也是s
【解决方案2】:

示例 3:(首先请注意,您必须使用 std::string *s 而不是 std::string s,因为 new 返回一个指针)。

使用 gdb 我在 std::cout 行设置断点并回溯这两个调用:

第一个是您在代码中编写的new。 第二个恰好是从 libstdc++.so.6 中调用的(在我的例子中):

(gdb) backtrace
#0  operator new (size=36) at test.cpp:6
#1  0x00007ffff7b78a89 in std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#2  0x00007ffff7b7a495 in char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7b7a5e3 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x0000000000400da1 in main () at test.cpp:20

【讨论】:

  • +1 不仅可以回答,还可以提示如何调查。
【解决方案3】:
  std::string s = "Hello world";

右手边是静态内存 s构造函数分配新内存并复制“Hello world”,

【讨论】:

    【解决方案4】:

    std::string 是一种封装 c 风格字符串的结构。

    为字符串容器结构进行第一次分配。
    第二个分配是用于在字符串构造中创建的字符串的副本。

    【讨论】:

      【解决方案5】:

      string 对象本身使用new 为实际字符分配空间。这是在字符串的构造函数中完成的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-10-09
        • 1970-01-01
        • 1970-01-01
        • 2015-09-15
        • 1970-01-01
        • 2021-03-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多