- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我正在使用旧的 C++03 代码库。一个部分看起来像这样:
#include <cstddef>
struct Pool
{ char buf[256]; };
struct A
{ virtual ~A() { } };
struct B : A
{
static void *operator new(std::size_t s, Pool &p) { return &p.buf[0]; }
static void operator delete(void *m, Pool &p) { } // Line D1
static void operator delete(void *m) { delete m; } // Line D2
};
Pool p;
B *doit() { return new(p) B; }
也就是说,B 派生自 A,但 B 的实例是从内存池中分配的。
(请注意,这个例子有点过于简单了......实际上,池分配器做了一些不平凡的事情,因此需要在 D1 行放置 operator delete
。)
最近,我们在更多编译器上启用了更多警告,第 D2 行引发以下警告:
warning: deleting ‘void*’ is undefined [-Wdelete-incomplete]
嗯,是的,很明显。但由于这些对象总是从池中分配的,我认为不需要自定义(非放置)operator delete
。所以我尝试删除 D2 行。但这导致编译失败:
new.cc: In destructor ‘virtual B::~B()’: new.cc:9:8: error: no suitable ‘operator delete’ for ‘B’ struct B : A ^ new.cc: At global scope: new.cc:18:31: note: synthesized method ‘virtual B::~B()’ first required here B *doit1() { return new(p) B; }
一点研究确定问题出在 B 的虚拟析构函数上。它需要调用非放置B::operator delete
,因为某个地方的某人可能会尝试通过A
。由于名称隐藏,第 D1 行使默认的非放置 delete
B
*operator delete
不可访问。
我的问题是:处理此问题的最佳方法是什么?一个明显的解决方案:
static void operator delete(void *m) { std::terminate(); } // Line D2
但这感觉不对……我的意思是,我是谁坚持让你必须从池中分配这些东西?
另一个明显的解决方案(以及我目前使用的):
static void operator delete(void *m) { ::operator delete(m); } // Line D2
但这也感觉不对,因为我怎么知道我调用了正确的删除函数?
我认为,我真正想要的是 using A::operator delete;
,但这不会编译(“在 'struct A' 中没有匹配 'A::operator delete' 的成员” )。
相关但不同的问题:
Why is delete operator required for virtual destructors
Clang complains "cannot override a deleted function" while no function is deleted
[更新,扩大一点]
我忘了提到 A
的析构函数在我们当前的应用程序中并不真的需要是 virtual
。但是从具有非虚拟析构函数的类派生会导致一些编译器在您提高警告级别时提示,而练习的初衷是消除此类警告。
另外,为了明确期望的行为......正常的用例如下所示:
Pool p;
B *b = new (p) B;
...
b->~B();
// worry about the pool later
也就是说,就像大多数使用placement new 一样,您可以直接调用析构函数。或者调用一个辅助函数来为你做这件事。
我不会期望以下工作;事实上,我认为这是一个错误:
Pool p;
A *b_upcast = new (p) B;
delete b_upcast;
检测到这种错误使用并失败是可以的,但前提是它可以在不对非错误情况增加任何开销的情况下完成。 (我怀疑这是不可能的。)
最后,我确实希望这会起作用:
A *b_upcast = new B;
delete b_upcast;
换句话说,我想支持但不要求为这些对象使用池分配器。
我目前的解决方案大多有效,但我担心直接调用 ::operator delete
不一定是正确的。
如果您认为您有充分的理由证明我对应该或不应该起作用的期望是错误的,我也想听听。
最佳答案
有趣的问题。如果我理解正确,您要做的就是根据它是否通过池分配来选择正确的删除运算符。
您可以在池中分配的 block 的开头存储一些额外的信息。
由于不能在没有池的情况下分配 B,因此您只需使用有关池的一些额外信息将其转发到普通 delete(void*) 运算符中的放置删除器。
Operator new 会将该部分存储在分配 block 的开头。
更新:感谢您的澄清。同样的技巧仍然适用于一些小的修改。更新了下面的代码。如果那仍然不是您想要做的,那么请提供一些正面和负面的测试用例来定义什么应该起作用,什么不应该起作用。
struct Pool
{
void* alloc(size_t s) {
// do the magic...
// e.g.
// return buf;
return buf;
}
void dealloc(void* m) {
// more magic ...
}
private:
char buf[256];
};
struct PoolDescriptor {
Pool* pool;
};
struct A
{
virtual ~A() { }
};
struct B : A
{
static void *operator new(std::size_t s){
auto desc = static_cast<PoolDescriptor*>(::operator new(sizeof(PoolDescriptor) + s));
desc->pool = nullptr;
return desc + 1;
}
static void *operator new(std::size_t s, Pool &p){
auto desc = static_cast<PoolDescriptor*>(p.alloc(sizeof(PoolDescriptor) + s));
desc->pool = &p;
return desc + 1;
}
static void operator delete(void *m, Pool &p) {
auto desc = static_cast<PoolDescriptor*>(m) - 1;
p.dealloc(desc);
}
static void operator delete(void *m) {
auto desc = static_cast<PoolDescriptor*>(m) - 1;
if (desc->pool != nullptr) {
throw std::bad_alloc();
}
else {
::operator delete (desc);
} // Line D2
}
};
Pool p;
void shouldFail() {
A* a = new(p)B;
delete a;
}
void shouldWork() {
A* a = new B;
delete a;
}
int main()
{
shouldWork();
shouldFail();
return 0;
}
关于c++ - 具有虚拟析构函数的池分配器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39336353/
有WHERE 1=1有什么作用如果您在脚本(伪代码)中编写此请求: sql = "SELECT f1,f2,f3 FROM t WHERE 1=1" ++ restOfTheClause
这个问题已经有答案了: R: Convert delimited string into variables (3 个回答) 已关闭 5 年前。 我有一个包含电影数据的表,在最后一列中,它包含电影所属
假设我有一个基类: struct A{ virtual void foo() = 0; }; 然后假设我有一个这样的派生类: struct B : public virtual A{ voi
我有一个小问题,我的 << 运算符没有被正确调用。 这是我的: class SomeInterface { friend std::ostream& operator<<(std::ostrea
首先,我来自 Java 社区,并且仍然是 C++ 的学习者。 请看下面的类 第二张图片显示了类“GameObject”的子类。它还有一个 Display() 方法。 GameObject类有5个子类,
我这里遇到了一些问题。我试图让我的代码像 java 中的接口(interface)一样工作。这个类被其他 2 个继承,因为它们导致了一些问题。而且我还想知道我是否做对了,以及改进我的代码的方法。我是新
在 C++ 中,我有一个基类 A,一个子类 B。两者都有虚方法 Visit。我想在 B 中重新定义“访问”,但 B 需要访问每个 A(以及所有子类)的“访问”功能。 我有类似的东西,但它告诉我 B 无
我有一个抽象类,它是类层次结构的根。该根类有一个带有一些简单实现的方法,似乎没有必要随时随地更改该实现。 使该方法成为非虚方法很好,但是某些子类可能会意外地重新实现它。在这种情况下,虚拟 final方
在 MSDN 上,我发现在抽象方法声明中使用“virtual”修饰符是错误的。我的一位同事应该是非常有经验的开发人员,但他在他的代码中使用了这个: public abstract class Busi
C++ 虚函数表是仅用于确定调用虚函数时应该执行哪一段代码,还是在运行时有其他用途? 在维基百科上,它列出了“动态调度”作为一个原因,但没有深入了解 C++ 的更多细节...... 最佳答案 一些实现
页面大小是否恒定?更具体地说,getconf PAGE_SIZE 给出 4096,这很公平。但这可以通过程序的运行时间改变吗?或者它在整个操作系统进程生成过程中是否保持不变。 IE。 , 进程是否可能
析构函数(当然还有构造函数)和其他成员函数之间的区别在于,如果常规成员函数在派生类中具有主体,则仅执行派生类中的版本。而在析构函数的情况下,派生版本和基类版本都会被执行? 很高兴知道在析构函数(可能是
如果一个函数被定义为虚函数并且与纯虚函数相同,这究竟意味着什么? 最佳答案 来自 Wikipedia's Virtual function... In object-oriented programm
我有一个在 Jetty 下运行的应用程序,我希望该应用程序返回自引用绝对 URL(生成 RSS 提要时,因此客户端必须能够在没有“当前 URL”上下文的情况下工作)。 问题是我事先不知道应用程序将部署
如何在两个virtualtreeview之间复制以复制所有列,而不仅仅是第一列? 复制前: 复制后: 最佳答案 树控件不保存任何数据。它不包含要显示的列数据,因此无法复制它。而是,当树控件想要显示任何
我已将 ShowHint 设置为 true 并将 HintMode 设置为 hmToolTip,但是当我将光标悬停在控件上时,我的 OnGetHint() 事件处理程序甚至没有断点。 知道我做错了什么
我的 friend 正在 Delphi 中使用 VirtualTreeView 工作,并且遇到了下一个问题:他有两列,第一列的每一行都有数据和子项。是否可以不更改第一列宽度来设置最大子列宽度? 图例:
我在我的 Virtual TreeView Component 中使用 TVirtualStringTree ( Delphi project 的一部分)我想创建一个 View ,其中 2 列可以有可
我想遍历 VirtualTreeView 的所有根并将其删除。 我不想清除它。 我收到此代码的访问冲突: var Node : PVirtualNode; begin if VirtualStri
我有一个可以输出表单的 PHP 文件。我想在服务器端调用这个 PHP 文件(当前使用“include”),填写并提交。 这样更好,因此我不必干预实际的 PHP 表单,只需处理表示层,以便数据可以被它自
我是一名优秀的程序员,十分优秀!