C++ 两个';新';动态创建std::string时调用

C++ 两个';新';动态创建std::string时调用,c++,memory,memory-management,C++,Memory,Memory Management,我正在尝试创建一个简单的内存管理器来熟悉这个概念,我已经为全局新建和删除提供了覆盖,并且刚刚开始构建一些对象,当我注意到对于单个动态字符串分配,我似乎要两次使用new。第一次命中new时,甚至在调用字符串构造函数之前,奇怪的是,这是两个分配中较大的一个,第二次命中时,调用来自std::string(basic_string)构造函数 我想知道这两个新的是关于什么的。具体地说,在这种情况下,我关心它,因为每一个新的都会创建自己的分配头,我对使用简单内存管理器会引入的开销有一种学术上的好奇心 相关代

我正在尝试创建一个简单的内存管理器来熟悉这个概念,我已经为全局新建和删除提供了覆盖,并且刚刚开始构建一些对象,当我注意到对于单个动态字符串分配,我似乎要两次使用new。第一次命中new时,甚至在调用字符串构造函数之前,奇怪的是,这是两个分配中较大的一个,第二次命中时,调用来自std::string(basic_string)构造函数

我想知道这两个新的是关于什么的。具体地说,在这种情况下,我关心它,因为每一个新的都会创建自己的分配头,我对使用简单内存管理器会引入的开销有一种学术上的好奇心

相关代码:

class DumbDataType
{
    std::string m_name;
};

int main()
{
    printf("STARTING MEMORY MANAGEMENT TESTING \n");

    printf("******************* Creating DATA ******************* \n");
    std::string* data = new std::string();
    delete data;

    printf("******************* Creating LORE ******************* \n");
    DumbDataType * lore = new DumbDataType();
    delete lore;

    getchar();
}
运行此操作时输出

启动内存管理测试
*******************正在创建数据********************
[新]28
[新]8
[删除]00D88C18
[删除]00D88BC8
*******************创造知识*******************
[新]28
[新]8
[删除]00D88C18
[删除]00D88BC8
运算符新建和删除

void * operator new(std::size_t size, MemoryManagement::Heap* heap)
{
    assert(heap != nullptr);
    assert(size > 0);
    return heap->Allocate(size);
}

void * operator new(std::size_t size)
{
    printf("[New] %i \n", size);
    return operator new (size, MemoryManagement::HeapFactory::GetDefaultHeap());
}

void operator delete (void * memoryLocation)
{
    if (memoryLocation != NULL)
    {
        printf("[Delete] %p \n", memoryLocation);
        MemoryManagement::Heap::Deallocate(memoryLocation);
    }
}
“GetDefaultHeap”方法只获取静态分配的堆数组中的第一个元素

为大小和标头分配足够的mallocs内存,并在标头偏移后返回正确的起始地址。 解除分配从它获得的内存地址中减去标头偏移量并释放该内存 (如果这些方法有用的话,我可以发布它们,只是看起来代码太多了)

这需要两个分配:一个用于
std::string
对象,另一个用于其底层内存缓冲区

如果你写

std::string s;
您将看到它只调用一次
new

As,这就是为什么有两次调用
new
。第一个
new
分配
std::string
对象并返回指向该对象的指针,但该
std::string
对象内部也有一个指针,构造该对象时还调用
new

class DumberDataType
{
public:
    DumberDataType()
    {
        i = new int;
    }

private:
    int * i;

};

class DumbDataType
{
    DumberDataType ddt;
};

int main()
{
    printf("STARTING MEMORY MANAGEMENT TESTING \n");

    printf("******************* Creating DATA ******************* \n");
    DumbDataType* data = new DumbDataType();

    getchar();
}
这段代码的输出是

启动内存管理测试
*******************创建数据*******************
[新]4
[新]4

您是否查看了
std::string
的构造函数以查看它是否进行了分配?@SebastianRedl是的,我查看了。正如我所说,第一个new甚至在它进入该构造函数之前发生,第二个new发生在该构造函数执行的过程中。因此,源代码中有两个
new
s,两个调用
操作符new
。现在问题是什么?@SebastianRedl,OP问为什么在源代码中每一个
new
调用都会发生两个
new
调用。有趣的是,有没有重复的地方,我可以阅读更多关于对象和缓冲区分配的内容?'std::string s;'那将是一个堆栈结构,对吗?这是否只是将字符串对象和缓冲区放在一个连续的分配中?@Vidrohi它会将字符串对象本身放在堆栈上(可能是指向缓冲区的指针、缓冲区的大小以及缓冲区中的数据字节数),但它几乎肯定不会尝试把缓冲区放在一个连续的分配中,因为这将要求所有的特殊情况贯穿整个代码> STD::String 代码,因为它不能被调整大小或释放。@ VIDROHY,你是说你试图写一个内存分配器,甚至不完全理解C++对象和内存模型。首先?@DavidSchwartz Cool,我很好奇,堆栈分配有何不同,比如,堆栈分配的字符串在哪里存储实际字符串和字符串对象本身?或者这是否意味着在堆栈分配情况下字符串对象不存在?
class DumberDataType
{
public:
    DumberDataType()
    {
        i = new int;
    }

private:
    int * i;

};

class DumbDataType
{
    DumberDataType ddt;
};

int main()
{
    printf("STARTING MEMORY MANAGEMENT TESTING \n");

    printf("******************* Creating DATA ******************* \n");
    DumbDataType* data = new DumbDataType();

    getchar();
}