草庐IT

c++ - 具有虚拟析构函数的池分配器

coder 2023-06-02 原文

我正在使用旧的 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 delete B *。由于名称隐藏,第 D1 行使默认的非放置 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/

有关c++ - 具有虚拟析构函数的池分配器的更多相关文章

  1. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  2. ruby - capybara field.has_css?匹配器 - 2

    我在MiniTest::Spec和Capybara中使用以下规范:find_field('Email').must_have_css('[autofocus]')检查名为“电子邮件”的字段是否具有autofocus属性。doc说如下:has_css?(path,options={})ChecksifagivenCSSselectorisonthepageorcurrentnode.据我了解,字段“Email”是一个节点,因此调用must_have_css绝对有效!我做错了什么? 最佳答案 通过JonasNicklas得到了答案:No

  3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  4. Ruby Koans about_array_assignment - 非平行与平行分配歧视 - 2

    通过ruby​​koans.com,我在about_array_assignment.rb中遇到了这两段代码你怎么知道第一个是非并行赋值,第二个是一个变量的并行赋值?在我看来,除了命名差异之外,代码几乎完全相同。4deftest_non_parallel_assignment5names=["John","Smith"]6assert_equal["John","Smith"],names7end45deftest_parallel_assignment_with_one_variable46first_name,=["John","Smith"]47assert_equal'John

  5. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  6. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  7. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  8. ruby - 在 Ruby 中重新分配常量时抛出异常? - 2

    我早就知道Ruby中的“常量”(即大写的变量名)不是真正常量。与其他编程语言一样,对对象的引用是唯一存储在变量/常量中的东西。(侧边栏:Ruby确实具有“卡住”引用对象不被修改的功能,据我所知,许多其他语言都没有提供这种功能。)所以这是我的问题:当您将一个值重新分配给常量时,您会收到如下警告:>>FOO='bar'=>"bar">>FOO='baz'(irb):2:warning:alreadyinitializedconstantFOO=>"baz"有没有办法强制Ruby抛出异常而不是打印警告?很难弄清楚为什么有时会发生重新分配。 最佳答案

  9. ruby-on-rails - Rails 3.1 中具有相同形式的多个模型? - 2

    我正在使用Rails3.1并在一个论坛上工作。我有一个名为Topic的模型,每个模型都有许多Post。当用户创建新主题时,他们也应该创建第一个Post。但是,我不确定如何以相同的形式执行此操作。这是我的代码:classTopic:destroyaccepts_nested_attributes_for:postsvalidates_presence_of:titleendclassPost...但这似乎不起作用。有什么想法吗?谢谢! 最佳答案 @Pablo的回答似乎有你需要的一切。但更具体地说...首先改变你View中的这一行对此#

  10. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

随机推荐