2014-09-05 69 views
4

我才发现,原来为basic_string<char>从在GCC 4.9实施STL(使用MinGW的-W64)的默认分配不使用全球operator new()但也许直接malloc,但在MSVC 11实施(VS 2012 )使用它。STL实现之间分配策略的区别?

还在CentOS下尝试了gcc 4.4.6,并且按预期调用了该运算符。

为什么会有这样的差异,而不是MSVC方法的正确方法?我希望能够通过提供我自己的operator new()来跟踪STL完成的每个分配。

这是我的测试代码:

#include <limits> 
#include <iostream> 
#include <vector> 
#include <string> 
#include <cstdlib> 
#include <memory> 
#include <map> 
using namespace std; 

template<typename T> 
struct MallocAllocator 
{ 
    typedef std::size_t size_type; 
    typedef std::ptrdiff_t difference_type; 
    typedef T* pointer; 
    typedef const T* const_pointer; 
    typedef T& reference; 
    typedef const T& const_reference; 
    typedef T value_type; 

    pointer allocate(size_type n, const void* = 0) { 
     cout << "Custom: " << sizeof(value_type) * n << endl; 
     return static_cast<pointer>(malloc(sizeof(value_type) * n)); 
    } 

    void deallocate(pointer ptr, size_type) { 
     free(ptr); 
    } 

    // boilerplate follows 
    MallocAllocator() {} 

    MallocAllocator(const MallocAllocator&) {} 

    size_type max_size() const throw() { return std::numeric_limits<std::size_t>::max()/sizeof(T); } 

    template <typename Other> 
    MallocAllocator(const MallocAllocator<Other>&) {} 

    MallocAllocator& operator=(const MallocAllocator&) { return *this; } 

    template <class Other> 
    MallocAllocator& operator=(const MallocAllocator<Other>&) { return *this; } 

    template <typename Other> 
    struct rebind { typedef MallocAllocator<Other> other; }; 

    pointer address(reference ref) const { 
     return &ref; 
    } 

    const_pointer address(const_reference ref) const { 
     return &ref; 
    } 

    void construct(pointer ptr, const value_type& val) { 
     ::new(ptr) value_type(val); 
    } 

    void destroy(pointer ptr) { 
     ptr->~value_type(); 
    } 
}; 

template <typename T, typename U> 
inline bool operator==(const MallocAllocator<T>&, const MallocAllocator<U>&) { 
    return true; 
} 

template <typename T, typename U> 
inline bool operator!=(const MallocAllocator<T>& a, const MallocAllocator<U>& b) { 
    return !(a == b); 
} 

void *operator new(size_t s) { 
    cout << "Global: " << s << endl; 
    return (void*)malloc(s); 
} 

void *operator new[](size_t s) { 
    cout << "Global: " << s << endl; 
    return (void*)malloc(s); 
} 

int main(int argc, char** argv) { 
    //basic_string<char, char_traits<char>, MallocAllocator<char> > s; 
    basic_string<char> s; 
    s = "dfasdf"; 
    s += "."; 
    s += "."; 
    s += "."; 
    s += "."; 
    s += "."; 
    s += "."; 
    s += "."; 
    s += "."; 
    s += "."; 
    s += "."; 
    s += "."; 
    s += "."; 
    s += "."; 
    s += "."; 
    s += "dfasdfsdfasfsdfasdfsdfasfsdfasdfsdfasfsdfasdfsdfasfsdfasdfsdfasfsdfasdfsdfasfsdfasdfsdfasfsdfasdfsdfasfsdfasdfsdfasfs"; 

    cout << s << endl; 

#ifdef _MSC_VER 
    system("pause"); 
#endif 

    return 0; 
} 

与MinGW的-W64 GCC:当我使用自定义的分配器,我看到了分配。当我不使用它时,我什么都看不到。

+0

FWIW - 它确实使用了全球新从GCC 4.8.1 - 见[ideone.com(http://ideone.com/Akj19D) - 必须是最近的变化。使我怀疑它是否试图'realloc'而不是每次新/复制/删除.... – 2014-09-05 08:41:07

+0

我刚刚尝试** ** gcc 4.8.1 **从** MinGW-w64 **,并再次没有工作。但从Linux下的普通gcc(任何版本),它的工作原理如下。 – onqtam 2014-09-05 08:47:20

+2

@blender。它可能是共享DLL中导致此错误的mingw-w64运行时。我会在周一再次选择它,并尝试更多静态和共享链接的组合等。我认为它们都支持在我的mingw-build中。 – Niall 2014-09-05 17:33:46

回答

3

我认为这可能只是在mingw-w64运行时或“mingw-builds”中的一个错误...

在“mingw-builds”版本中使用的运行时有可能导出使用std::string与默认分配器从一个预先构建的DLL(我还没有能够证实这一点,它是可能的mingw-w64运行时本身),MSVC在过去做了类似的事情(尽管我不知道这是否会导致类似的错误)。

我已经尝试了一些组合:

  • 的“MinGW的,建立”建设使用vector和覆盖operator new如预期使用。
  • 使用静态绑定(--static)将可执行文件(mingw-builds)构建到运行时可以按预期工作(对于stringvector)。
  • “nuwen”版本和stringvector的预期工作。
  • VS2013也调用覆盖的operator newstd::allocator一起使用(有和没有/MD选项)。
  • VS2008(与/MD)产生意外输出(std::string从运行时dll,IIRC导出)。

从我可以辨认出std::string是从“的libstdC++ - 6.dll”出口,所以::operator new等的结合可能已经在二进制为string分配固定的(因为你现在知道)。

对分配器的简单改变;

template <class C> 
struct myallocator : std::allocator<C> { 
}; 

允许重写全球operator new作为工作的“MinGW的,建立”期望。你已经注意到了你完全的自定义分配器,但是这里的简单派生支持“默认”字符串类可能来自dll的概念。

#include <cstdio> 
#include <cstdlib> 
#include <string> 
#include <iostream> 
#include <vector> 
// replacement of a minimal set of functions: 
void* operator new(std::size_t sz) { 
    std::printf("global op new called, size = %d\n",sz); 
    return std::malloc(sz); 
} 
void operator delete(void* ptr) noexcept 
{ 
    std::puts("global op delete called"); 
    std::free(ptr); 
} 
template <class C> 
struct myallocator : std::allocator<C> { 
}; 
int main() { 
    using namespace std; 

    vector<int> def; 
    def.resize(100000); 

    basic_string<char, char_traits<char>, myallocator<char>> abc; 
    abc.resize(100000); 
} 

命令行

​​
+2

链接到已经由某人提交的mingw-w64错误报告:) http://sourceforge.net/p/mingw-w64/bugs/422/ – Niall 2014-09-05 10:15:00

1

n3376 20.6.9.2

pointer allocate(size_type n, allocator<void>::const_pointer hint = 0);

备注:存储是通过调用::运算新(标准::的size_t) (18.6.1)中得到,但它是UNSPEC - 当调用此函数的次数或次数为 时。提示的使用是未指定的,但如果实现需要,则用作对局部性的辅助。

你确定没有调用operator new吗?

-1

也许我可能是错的,但在Windows上实现operator new()直接或间接调用malloc(),其调用函数如VirtualAlloc来实现OS的实际内存分配。内存分配没有其他方式,您可能对此保持冷静。

0

gcc basic_string类确实有模板的参数allocator。这意味着您可以通过一个小的更改(到stringfwd.h)更新std::string以使用不同的分配方法,包括使用new的分配方法。

凡说:

typedef basic_string<char> string; 

将其更改为:

typedef basic_string<char, char_traits<char>, my_allocator> string; 

虽然我预计你可能只看到了实现优化,并使用了小弦一些内部存储 - 一次你需要足够大的字符串,我应该使用new进行分配。

+0

我尝试了多达138个字符串(连接),仍然没有调用'''operator new()''' – onqtam 2014-09-05 07:45:44

+1

如果我使用自定义分配器,我会调用一些allocate()函数。我猜测我不应该使用自定义分配器的时候,对'''operator new()''调用相同数量的调用。 – onqtam 2014-09-05 08:32:48

+0

是的,假设默认分配器确实使用'new',那么对operator new()的调用次数相同。 – 2014-09-05 21:16:15