了解c++中的库函数

发布于 2024-08-22 19:15:53 字数 90 浏览 5 评论 0原文

如果我想知道用标准 C++ 库编写的函数是如何工作的(不仅仅是 MSDN 描述)。我的意思是它如何分配、管理、释放内存并返回结果。您需要知道哪里或什么才能理解这一点?

If I'd like to know how a function written in like standard C++ library work (not just the MSDN description). I mean how does it allocate, manage, deallocate memory and return you the result. where or what do you need to know to understand that?

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

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

发布评论

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

评论(5

热情消退 2024-08-29 19:15:53

您可以查看库标题。许多功能实际上是在那里实现的,因为该库是高度模板化的(并且模板通常需要在标头中实现)。标头的位置取决于编译器,但您应该能够很容易地找到它们(例如搜索名为算法的文件)。

您还可以要求编译器预处理您的代码以查看所有相关的标头(这将产生极长的输出)。使用 GCC,您可以通过 g++ -E yoursource.cc 来完成此操作。

如果您要查找的内容未在标头中实现,则需要库源,这些库源通常默认情况下不会安装,甚至不适用于 MSVC 等商业编译器。查找 glibc(C 库)和 libstdc++(C++ 库),它们是 GCC 和其他一些编译器使用的库。

无论如何,请注意,由于在变量名称等中使用了大量下划线(以避免与用户宏的名称冲突),标准库实现往往相当神秘,并且它们通常还充斥着 #ifdefs 和其他预处理器粗鲁的。

You can look at the library headers. A lot of functionality is actually implemented there because the library is highly templatized (and templates generally need to be implemented in headers). The location of the headers depends on the compiler, but you should be able to find them quite easily (e.g. search for a file named algorithm).

You may also ask the compiler to preprocess your code to see all the related headers (this will produce extremely long output). With GCC you can do this by g++ -E yoursource.cc.

If what you are looking for isn't implemented in headers, you need the library sources, which are generally not installed by default and which are not even available for commercial compilers such as MSVC. Look for glibc (C library) and libstdc++ (C++ library), which are the ones used by GCC and some other compilers.

In any case, notice that the standard library implementations tend to be rather cryptic due to a lot of underscores being used in variable names and such (to avoid name collisions with user's macros), and often they are also infested with #ifdefs and other preprocessor cruft.

甜中书 2024-08-29 19:15:53

您需要了解用于编写 C++ 库的技术。获取 Bjarne Stroustrup 的 书是一个好的开始。此外,SGI 在适当的高抽象级别上有关于 STL 的非常详细的文档。

如果您要研究基于 Windows 的内容,您可能需要研究 Windows 库的系统部分。

为了补充 Windows:了解 Posix 规范也很重要。

You need to know the techniques used to write C++ libraries. Getting Bjarne Stroustrup's book is a good start. Also, SGI has very detailed documentation on the STL at a suitably high level of abstraction.

If you are going to be investigating the windows based stuff you might want to study the systems part of the windows library.

To complement windows: understanding the Posix specification is also important.

不即不离 2024-08-29 19:15:53

首先是一些基本的数据结构原理,然后是关于分配器的注释和一些链接...

STL 容器使用许多不同的数据结构。例如,映射、集合、多重映射和多重集合通常实现为具有红黑平衡规则的二叉树,而双端队列可能(更多的是印象而不是知识)数组中的循环队列,利用数组加倍或类似的增长模式。

标准实际上没有定义任何数据结构 - 但指定的性能特征极大地限制了选择。

通常,您包含的数据直接包含在数据结构节点中,这些节点(默认情况下)保存在堆分配的内存中。您可以通过在指定容器时提供分配器模板参数来覆盖节点的内存源 - 稍后将详细介绍。如果您需要容器节点来引用(不包含)您的项目,请指定指针或智能指针类型作为包含的类型。

例如,在 std::set 中,节点将是二叉树节点,其中包含用于 int 和两个子指针以及库所需的元数据(例如红/黑标志)的空间。二叉树节点不会在您的应用程序地址空间中移动,因此您可以根据需要将指向数据项的指针存储在其他位置,但并非所有容器都如此 - 例如,向量中的插入会将所有项目移动到插入上方点向上一,并且可能必须重新分配整个向量,移动所有项目。

容器类实例通常非常小——典型的是几个指针。例如, std::set 等通常有一个根指针、一个指向最低键节点的指针和一个指向最高键节点的指针,可能还有更多元数据。

STL 面临的问题之一是在多项目节点中创建和销毁实例,而不创建/销毁节点。例如,这种情况发生在 std::vector 和 std::deque 中。严格来说,我不知道 STL 是如何做到这一点的 - 但明显的方法需要放置新的和显式的析构函数调用。

放置 new 允许您在已分配的内存中创建对象。它基本上为您调用构造函数。它可以接受参数,因此它可以调用复制构造函数或其他构造函数,而不仅仅是默认构造函数。

要销毁,您实际上是通过(正确键入的)指针显式调用析构函数。

((mytype*) (void*) x)->~mytype ();

如果您没有声明显式构造函数,甚至对于像“int”这样不需要解构的内置类型,这也是有效的。

同样,要将一个构造实例分配给另一个构造实例,您可以显式调用operator=。

基本上,容器能够相当轻松地在现有节点内创建、复制和销毁数据,并且在需要时,元数据会跟踪节点中当前构造的项目 - 例如 size() 指示当前在 std:: 中构造哪些项目向量 - 可能有额外的非构造项目,具体取决于当前的容量()。

编辑 - STL 可以通过使用(直接或实际上)std::swap 而不是operator= 来移动数据来进行优化。当数据项是(例如)其他 STL 容器时,这会很好,因此拥有大量引用的数据 - 交换可以避免大量复制。我不知道标准是否要求这样做,或者允许但不强制这样做。不过,有一种众所周知的机制可以使用“特征”模板来完成此类操作。默认“特征”可以提供分配使用方法,而特定覆盖可以通过使用交换方法来支持特殊情况类型。抽象将是一种移动,您不关心源中剩下的内容(原始数据、来自目标的数据等),只要它有效且可破坏即可。

当然,在二叉树节点中,应该不需要这样做,因为每个节点只有一个项目,并且它总是被构造的。

剩下的问题是如何在节点结构中保留正确对齐和正确大小的空间来保存未知类型(指定为模板参数),而不会在创建/销毁节点时调用不需要的构造函数/析构函数。这在 C++0x 中会变得更容易,因为联合将能够保存非 POD 类型,从而提供方便的未初始化空间类型。在那之前,有一系列的技巧或多或少地具有不同程度的可移植性,毫无疑问,良好的 STL 实现是一个值得学习的好例子。

就我个人而言,我的容器使用 space-for-type 模板类。它使用特定于编译器的分配检查来确定编译时的对齐情况,并使用一些模板技巧来从正确大小的字符数组、短数组、长数组等中进行选择。使用“#if Defined”等选择不可移植的对齐检查技巧,当有人向模板提出 128 位对齐要求时,模板将失败(在编译时),因为我还不允许这样做。

实际如何分配节点?好吧,大多数(全部?)STL 容器都采用“Allocator”参数,默认为“allocator”。该标准实现从堆中获取内存并将其释放到堆中。实现正确的接口,它可以用自定义分配器替换。

这是我不喜欢做的事情,而且如果我桌上没有 Stroustrups 的《C++ 编程语言》,我当然不会这么做。分配器类需要满足很多要求,至少在过去(情况可能有所改善),编译器错误消息没有有帮助。

谷歌说你可以看看这里...

First a few basic data-structure principles, then a note and some links about allocators...

The STL containers use a number of different data structures. The map, set, multimap and multiset are normally implemented as binary trees with red-black balancing rules, for example, and deque is possibly (more impression than knowledge) a circular queue in an array, exploiting an array-doubling or similar growth pattern.

None of the data structures are actually defined by the standard - but the specified performance characteristics limit the choices significantly.

Normally, your contained data is contained directly in the data structure nodes, which are held (by default) in heap allocated memory. You can override the source of memory for nodes by providing an allocator template parameter when you specify the container - more on that later. If you need the container nodes to reference (not contain) your items, specify a pointer or smart-pointer type as the contained type.

For example, in an std::set, the nodes will be binary tree nodes with space in them for an int and the two child pointers, and the metadata that the library needs (e.g. the red/black flag). The binary tree node will not move around your applications address-space, so you can store pointers to your data item elsewhere if you want, but that isn't true for all containers - e.g. an insert in a vector moves all items above the insert point up by one, and may have to reallocate the whole vector, moving all items.

The container class instance is normally very small - a few pointers is typical. For example, the std::set etc usually have a root pointer, a pointer to the lowest-key node and a pointer to the highest-key node, and probably a bit more metadata.

One issue the STL faces is creating and destroying instances in multi-item nodes without creating/destroying the node. This happens in std::vector and std::deque, for instance. I don't know, strictly, how the STL does it - but the obvious approach requires placement new and explicit destructor calls.

Placement new allows you to create an object in an already-allocated piece of memory. It basically calls the constructor for you. It can take parameters, so it can call a copy constructor or other constructor, not just the default constructor.

To destruct, you literally call the destructor explicitly, via a (correctly typed) pointer.

((mytype*) (void*) x)->~mytype ();

This works if you haven't declared an explicit constructor, and even for built-in types like "int" that don't need destructing.

Likewise, to assign from one constructed instance to another, you make an explicit call to operator=.

Basically, the containers are able to create, copy and destroy data within an existing node fairly easily, and where needed, metadata tracks which items are currently constructed in the node - e.g. size() indicates which items are currently constructed in an std::vector - there may be additional non-constructed items, depending on the current capacity().

EDIT - It's possible that the STL can optimise by using (directly, or in effect) std::swap rather than operator= to move data around. This would be good where the data items are (for example) other STL containers, and thus own lots of referenced data - swapping could avoid lots of copying. I don't know if the standard requires this, or allows but doesn't mandate it. There is a well-known mechanism for doing this kind of thing, though, using a "traits" template. The default "traits" can provide an assignment-using method whereas specific overrides may support special-case types by using a swapping method. The abstraction would be a move where you don't care what is left in the source (original data, data from target, whatever) as long as it's valid and destructible.

In binary tree nodes, of course, there should be no need for this as there is only one item per node and it's always constructed.

The remaining problem is how to reserve correctly-aligned and correctly-sized space within a node struct to hold an unknown type (specified as a template parameter) without getting unwanted constructor/destructor calls when you create/destroy the node. This will get easier in C++0x, since a union will be able to hold non-POD types, giving a convenient uninitialised-space type. Until then, there's a range of tricks that more-or-less work with different degrees of portability, and no doubt a good STL implementation is a good example to learn from.

Personally, my containers use a space-for-type template class. It uses compiler-specific allocation checks to determine the alignment at compile-time and some template trickery to choose from an array-of-chars, array-of-shorts, array-of-longs etc of the correct size. The non-portable alignment-checking tricks are selected using "#if defined" etc, and the template will fail (at compile time) when someone throws a 128-bit alignment requirement at it because I haven't allowed for that yet.

How to actually allocate the nodes? Well, most (all?) STL containers take an "Allocator" parameter, which is defaulted to "allocator". That standard implementation gets memory from and releases it to the heap. Implement the right interface and it can be replaced with a custom allocator.

Doing that is something I don't like to do, and certainly not without Stroustrups "The C++ Programming Language" on my desk. There's a lot of requirements to meet in your allocator class, and at least in the past (things may have improved), compiler error messages were not helpful.

Google says you could look here, though...

月下凄凉 2024-08-29 19:15:53

分配/释放内存的操作系统函数与 C++ 标准库并不真正相关。

标准库容器(默认情况下)将使用 new 和 delete 来存储内存,并且使用特定于编译器的运行时,该运行时几乎肯定会管理自己的堆数据结构。这种方法通常更适合典型的应用程序使用,其中特定于平台的操作系统堆通常更适合分配大块。

应用程序堆将从操作系统堆中分配/释放内存,但是“如何?”和“什么时候?”是特定于平台和特定于编译器的详细信息。

对于 Win32 内存管理 API,请查看此处...

http://msdn. microsoft.com/en-us/library/ms810603.aspx

我确信您可以根据需要找到 win64 等效项。

Operating system functions to allocate/free memory are not really relevant to the C++ standard library.

The standard library containers will (by default) use new and delete for memory, and that uses a compiler-specific runtime which almost certainly manages its own heap data structure. This approach is generally more appropriate for typical applications use, where the platform-specific operating system heap is usually more appropriate for allocating large blocks.

The application heap will allocate/free memory from the operating system heap, but "how?" and "when?" are platform-specific and compiler-specific details.

For the Win32 memory management APIs, look here...

http://msdn.microsoft.com/en-us/library/ms810603.aspx

I'm sure you can find win64 equivalents if needed.

银河中√捞星星 2024-08-29 19:15:53

我没有这本书,但根据其描述, http://www.amazon .com/C-Standard-Template-Library/dp/0134376331 包括

-使用和实现组件的实用技术

这不是您想要的吗?

I haven't this book, but according to its description, http://www.amazon.com/C-Standard-Template-Library/dp/0134376331 includes

-Practical techniques for using and implementing the component

Isn't this what you want?

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