为什么不使用模板实现新内容?
Why isn't new implemented with template?
我试图用一些额外的信息来检测new
,以便追踪内存泄漏。我知道,我可以全局覆盖new
操作符,但我惊讶地发现,我无法检索到有关被分配对象类型的任何信息(如果我错了请纠正我)。显然,当您决定覆盖new
操作符时,拥有类型信息将是有益的。
例如,我使用可变模板实现了new
和delete
的简单通用版本。
std::string to_readable_name(const char * str)
{
int status;
char *demangled_name = abi::__cxa_demangle(str, NULL, NULL, &status);
if(status == 0) {
std::string name(demangled_name);
std::free(demangled_name);
return name;
}
return "Unknown";
}
template <typename T, typename... Args>
T * generic_new(Args&&... args) throw (std::bad_alloc)
{
const std::size_t size = sizeof(T);
std::cout << "Allocating " << size << " bytes for " << to_readable_name(typeid(T).name()) << std::endl;
return new T(std::forward<Args>(args)...);
};
template<typename T>
void generic_delete(T* ptr)
{
const std::size_t size = sizeof(T);
std::cout << "Deleting " << size << " bytes for " << to_readable_name(typeid(T).name()) << std::endl;
delete ptr;
}
int main()
{
auto i = generic_new<int>(0);
std::cout << *i << std::endl;
generic_delete(i);
return 0;
}
我的问题是为什么没有new
与模板实现?这将允许开发人员获得有关被分配对象类型的信息。
谢谢
大多数关于为什么c++是如何设计的问题归结为两种可能性。 c++的设计和发展给出了一个原因,或者我们只能推测。我相信这属于后一类。
首先,operator new
(全局的和每类的)的使用(和替换的能力)早于模板添加到语言中。因此,最初是不可能用模板完成的。库的一些部分被切换到以前没有的模板,但大多数情况下这样做的好处是非常明显的。
第二,在早期具有模板功能的编译器中使用模板可能会导致问题,特别是对于较大的程序。问题在于函数模板不是函数——可以说,它是创建函数的配方。换句话说,如果将其实现为模板,则每种类型的每个分配器都将导致实例化一个单独的函数。当前的编译器(和链接器)非常擅长事后合并,但早期的编译器大多不是这样。在一个大型系统中,你可以很容易地创建几十个甚至几百个独立的分配函数。
最后,模板通常仍然只是介于普通用户代码和库中的之间的中间层,它提供了至少与当前operator new
和operator delete
大致一致的东西,它们与操作系统(或硬件,或其他)交谈,以确定从哪个池中提取内存,并(通常)将块子分配给用户代码。在某个地方,您需要一段(通常相当可观的)代码来创建和管理内存池。如果在模板中直接处理这个问题,它将(在实践中)必须在头文件中实现,这也将导致编译时间,这在66 MHz的Pentium上可能是相当不可接受的,甚至在我们大多数人只能梦想的令人难以置信的300 MHz DEC Alpha机器上。
可以not重写全局操作符new。可以做的是提供一个替换——这发生在链接期间,而不是在编译期间。
我的问题是为什么没有新的实现与模板?
因为new
基本上是一个未定义的引用,你可以很容易地提供一个定义来代替它。(见下面的细节#1)
现在,如果new
是一个模板函数会是什么?对于每个模板实例,都有一个不同的未定义符号,您需要为其提供替换。您也可以使您的定义成为模板,但是谁来实例化它呢?您可能需要为此更改名称解析规则(以及模板的基本工作方式)。
或者允许operator new
被覆盖。然后编译器必须在编译期间选择正确的operator new
。但这有一个缺点,即只有在其翻译单元中包含"新new
"的代码才能看到并使用它。现在考虑这里发生了什么:
#include <memory>
// code to override new and delete
void bar(void) {
std::unique_ptr<int> x = new int{};
}
上面对new
的调用使用了您的代码,但是对delete
的调用深藏在标准库中呢?该调用也需要看到您的代码。反过来,这意味着标准库代码必须在您的翻译单元中,或者换一种说法:它需要在头文件中实现。
此外,您需要确保使用new
分配的char *
不会通过"标准"delete
获得delete
d。类特定的操作符
class Baz; // forward declaration
void freeThatBaz(Baz * b) { delete b; }
这实际上是调用类特定的操作符delete
还是全局操作符,这是实现定义的(从c++ 17开始,这是IIRC之前未定义的行为)。
所以我想说:基本上,不值得在标准中制定如此庞大而复杂的规则,甚至不被认为是"良好实践"。
# 1细节
编译以下代码:
void foo(void) {
auto x = new int{};
auto y = new bool{};
delete x;
delete y;
}
导致
[nix-shell:/tmp/wtf]$ nm -C new.o
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T foo()
U operator delete(void*, unsigned long)
U operator new(unsigned long)
对operator new
的一个未定义引用,与类型无关。如果是模板,则模板类型参数将是符号的一部分,因此您将有两个未定义的符号。
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 如何逐行读取文件,每行中的内容都用空格分隔并将其写入新文件中
- 如何在输入新内容 c++ 之前删除文件中的所有内容
- C++ 如何使用动态计算的新节点实现 A*?
- 实现一个函数,该函数将字符串作为输入并返回一个新字符串,辅音字母不替换为 "!"
- 为什么我的代码在终端中没有输出任何内容.开始新行没有错误
- 为什么这个新的 [ ] 和删除 [ ] 实现会分解为 12 >整数?
- 在不创建新节点的情况下实现带有映射的trie
- ifstream在OSX上看不到新内容
- 使用 Qt5 的新信号/插槽实现向滑块发出信号
- 如何实现由TPAINTBOX创建的新组件的OnMousedown,OnMouseUp事件
- 为什么我不能使私人运营商成为新的并使用默认实现?
- 如果新内容过载但未加载相应的删除,会发生什么情况
- 不完整类型的新内容在包装在模板中时编译
- 我是否需要对C++中的每个新内容使用删除
- 通过工厂设计模式向客户隐藏新内容的优势是什么
- C++ 单个指针上的多个新内容
- c++类模板专门化,无需重新实现所有内容
- 与将新内容分配给向量的指针斗争
- 为什么不使用模板实现新内容?