什么是“哨兵对象”?在 C++ 中?

发布于 2024-08-29 16:18:13 字数 756 浏览 2 评论 0原文

回答了这个问题 和 Potatoswatter 也回答了

现代 C++ 的等价物是 哨兵对象:构造它 函数的开头,及其 实现 call() 的构造函数,以及 返回(或异常退出)时,其 析构函数实现

我不熟悉在 C++ 中使用哨兵对象。 我认为它们仅限于输入和输出流。

有人可以向我解释一下 C++ 哨兵对象以及如何将它们用作类中一个或多个方法的周围拦截器吗?

即如何做到这一点?

Sentry 对象非常相似 的确。一方面他们要求 显式实例化(并且是 通过了这个)但另一方面你 可以添加到它们中,以便它们检查不 只有类的不变量,但是 一些前置/后置条件 手头的功能。

I answered this question, and Potatoswatter answered too as

The modern C++ equivalent would be a
sentry object: construct it at the
beginning of a function, with its
constructor implementing call(), and
upon return (or abnormal exit), its
destructor implements

I am not familiar with using sentry objects in C++.
I thought they were limited to input and output streams.

Could somebody explain to me about C++ sentry objects as well as how to use them as an around interceptor for one or more methods in a class ?

i.e. How to do this ?

Sentry objects are very similar
indeed. On the one hand they require
explicit instantiation (and being
passed this) but on the other hand you
can add to them so that they check not
only the invariants of the class but
some pre/post conditions for the
function at hand.

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

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

发布评论

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

评论(2

帝王念 2024-09-05 16:18:13

Sentry 对象是一种模式,但我不确定下面的哪一个(也许全部)。

C++ 程序通常严重依赖于知道对象(可能是用户定义的类)何时被销毁,即何时调用其析构函数。对于具有垃圾收集功能的语言来说,情况并非如此。

例如,该技术用于拥抱“资源获取即初始化”范例:在调用对象构造函数时获取资源,编译器会在正常和异常(异常)情况下自动调用其析构函数来释放资源(检查 < a href="https://stackoverflow.com/questions/712639/please-help-us-non-c-developers-understand-what-raii-is">这个问题)。

可以利用构造/销毁计时知识的常见位置是

  • :在块的末尾调用“堆栈分配”对象的析构函数

    void 函数()
    { 类 foo = 对象(资源);
       其他操作();
    } // 此处调用 foo 的析构函数
    
  • 函数调用:“stack-当您调用函数时也会发生分配”

    void 函数()
    { another_function ( 类(资源) );
       // 调用未命名对象的析构函数
       // another_function() 返回(或抛出)之后
       其他操作();
    }
    
  • 包含对象的构造/销毁

    类 Foo
    { 班级哨兵;
       公共:Foo()
       { // 这里调用哨兵的构造函数
          某物();
       }        
       公共:~Foo()
       {
          某物();
       } // 这里调用哨兵的析构函数
    };
    

在STL中,有一个名为sentry的类(更准确地说,istream::sentry),它实现了上述的第三种模式。所以我认为这就是一些程序员所说的“哨兵对象”。

但事实上,上述Class类的任何对象都可以称为“哨兵对象”。它们是“哨兵”,因为它们确保即使有异常抛出,也不会错过这些难以捉摸的对象析构函数(因此它们就像块/类的守护者)。

更多哨兵对象示例位于 RAII 问题


您可以看到与面向方面编程的关系;这些对象类似于“方面”,具有“在封闭块的开始/结束处”、“在包含对象的构造/销毁时”等切点。但是这些“方面”必须存在于他们关注的代码。因此,与原始的调用/返回功能相比,它们的“视角”较少;相反,应该将哨兵对象插入到该类的每个函数中:

class X{
  struct Sentry {
     Sentry() { /* call() */}
    ~Sentry() { /* return() */};
  };

  void member_function()
  { Sentry();
    /* operations */
  }

  void another_member_function()
  { Sentry();
    /* operations */
  }
};

Sentry object is a pattern, but I'm not sure which one of those below (maybe all).

C++ programs often heavily rely on knowledge when exactly an object (possibly of a user-defined class) is destroyed, i.e. when its destructor is called. This is not the case for languages with garbage collection.

This technique is used, for example, to embrace "Resource Acquisition Is Initialization" paradigm: you acquire resources when an object constructor is called, and the compiler automatically calls its destructor to free resources in both normal and abnormal (exceptional) situations (check this question).

Common places where you can utilize the knowledge of construction/destruction timing are

  • Blocks: a destructor for "stack-allocated" object is called at the end of the block

    void function()
    {  Class foo = Object(resource);
       other_operations();
    }  // destructor for foo is called here
    
  • Function calls: "stack-allocation" also happens when you call a function

    void function()
    {  another_function ( Class(resource)  );
       // destructor for the unnamed object is called
       // after another_function() returns (or throws)
       other_operations();
    }
    
  • Construction/Destruction of the containing object:

    class Foo
    {  Class sentry;
       public: Foo()
       { // Constructor for sentry is called here
          something();
       }        
       public: ~Foo()
       {
          something();
       }  // destructor for sentry is called here
    };
    

In STL there's a class called sentry (more exactly, istream::sentry), which implements the third pattern of those described above. So I think that is what some programmers refer to as "sentry object".

But in fact any of the above objects of class Class may be called "sentry object". They're "sentry" because they ensure that these elusive object destructors are not missed even if something throws an exception (so they are like guardians of the block/class, of sorts).

More sentry object examples are in that RAII question.


You can see a relation to aspect-oriented programming; these objects are something like "aspects", with cutpoints "at the beginning/ending of the enclosing block", "at construction/destruction of containing object" etc. But these "aspects" have to present in the code they aspectate. So they're less "aspective" compared to the original call/return functionality; instead, a sentry object should be inserted to each function of the class:

class X{
  struct Sentry {
     Sentry() { /* call() */}
    ~Sentry() { /* return() */};
  };

  void member_function()
  { Sentry();
    /* operations */
  }

  void another_member_function()
  { Sentry();
    /* operations */
  }
};
逆光飞翔i 2024-09-05 16:18:13

与 AOP 的区别在于,它必须通过将哨兵显式地放置在函数体或类定义内的某个位置来协作来完成。

如果不修改目标函数或类,您就无法捕获调用。

The difference with AOP is that it has to be done cooperatively by putting the sentry explicitly somewhere inside the function body or class definition.

You cannot catch the calls without modifying the targeted function or class.

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