代码之家  ›  专栏  ›  技术社区  ›  SCFrench

ScopeGuard的使用真的会带来更好的代码吗?

  •  32
  • SCFrench  · 技术社区  · 17 年前

    我偶然发现 this article

    7 回复  |  直到 16 年前
        1
  •  62
  •   R. Martinho Fernandes    11 年前

    它肯定会改进您的代码。您尝试性地表述了这样一种说法,即它是模糊的,并且代码应该从 catch 在C++中,块是不正确的,因为RAII是一个已建立的习惯用法。C++中的资源处理 通过资源获取完成,垃圾收集通过隐式析构函数调用完成。

    另一方面,明确的 接住

    RAII(包括 ScopeGuard

        2
  •  29
  •   j_random_hacker    17 年前

    如果有一块C++代码,我可以建议每一个C++程序员花10分钟学习,它是ScopeGuard(现在是免费提供的一部分)。 Loki library

    我决定尝试将ScopeGuard的一个(稍加修改)版本用于我正在开发的一个小型Win32 GUI程序。您可能知道Win32有许多不同类型的资源,需要以不同的方式关闭(例如,内核句柄通常使用 CloseHandle() ,GDI BeginPaint() EndPaint() 等)我使用ScopeGuard处理所有这些资源,还使用ScopeGuard分配工作缓冲区 new (例如,用于与Unicode之间的字符集转换)。

    更短的 这个节目很成功。 基本上,这是一种双赢:您的代码会变得更短,同时更健壮。未来的代码更改 . 他们就是不能。那有多酷?

        3
  •  2
  •   1800 INFORMATION    17 年前

    我经常用它来保护内存使用,这些需要释放的东西是从操作系统返回的。例如:

    DATA_BLOB blobIn, blobOut;
    blobIn.pbData=const_cast<BYTE*>(data);
    blobIn.cbData=length;
    
    CryptUnprotectData(&blobIn, NULL, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &blobOut);
    Guard guardBlob=guardFn(::LocalFree, blobOut.pbData);
    // do stuff with blobOut.pbData
    
        4
  •  2
  •   timepp    9 年前

    void somefunction() {
        writeln("function enter");
        // c++ has similar constructs but not in syntax level
        scope(exit) writeln("function exit");
    
        // do what ever you do, you never miss the function exit output
    }
    
        5
  •  1
  •   Leon Timmermans    17 年前

    我没有使用过这个特定的模板,但我以前也使用过类似的模板。是的,与以不同方式实现的同样健壮的代码相比,它确实会带来更清晰的代码。

        6
  •  1
  •   j_kubik    10 年前

    我认为上述答案缺少一个重要的提示。正如其他人指出的,您可以使用 ScopeGuard 以释放独立于故障(异常)的已分配资源。但这可能不是您使用scope guard的唯一目的。事实上,链接文章中的示例使用 为了不同的目的:翻译。简而言之,如果有多个对象(即使这些对象正确地使用了RAII)需要保持某种相关状态,那么它可能会很有用。如果这些对象中任何一个的状态改变导致异常(我认为,这通常意味着它的状态没有改变),那么所有已经应用的更改都需要回滚。这就产生了它自己的一系列问题(如果回滚也失败了怎么办?)。您可以尝试推出自己的类来管理此类相关对象,但随着这些对象数量的增加,它会变得混乱,您可能会重新使用 示波器保护器

        7
  •  -1
  •   Puppy    10 年前

    如果 范围保护有任何实际用途,资源处理是 其中一个。在这种情况下,它们大大低于普通RAII,因为RAII是重复数据消除和自动的,范围保护是手动代码复制或半身像。

        8
  •  -2
  •   Grim Fandango    5 年前

    我的经验表明 scoped_guard 远不如任何可以手工编写的短可重用RAII类。

    内窥镜防护罩 ,我写了RAII课程给

    • 绘制形状后,将GLcolor或GLwidth设置回原始值
    • 确保文件已保存 fclose 我曾经 fopen 我把它弄坏了。
    • 重置 sorting 一旦我临时完成了对QListView的修改,它的状态将恢复到以前的状态 QListViewItems --我不希望每次更改单个项目的文本时列表都会重新排序。。。

    使用简单的RAII类

    class scoped_width {
        int m_old_width;
    public:
        scoped_width(int w) {
            m_old_width = getGLwidth();
            setGLwidth(w);
        }
        ~scoped_width() {
            setGLwidth(m_old_width);
        }
    };
    
    void DrawTriangle(Tria *t)
    {
        // GLwidth=1 here
    
        auto guard = scoped_width(2); // sets GLwidth=2
    
        draw_line(t->a, t->b);
        draw_line(t->b, t->c);
        draw_line(t->c, t->a);
    
        setGLwidth(5);
    
        draw_point(t->a);
        draw_point(t->b);
        draw_point(t->c);
    
    }  // scoped_width sets GLwidth back to 1 here
    

    非常简单的实现 scoped_width ,而且可重复使用。 从消费者的角度来看,也非常简单易读。

    内窥镜防护罩 (C++14)

    现在,随着 ,我必须捕获介绍人中的现有值( [] )要将其传递给警卫的回调,请执行以下操作:

    void DrawTriangle(Tria *t)
    {
        // GLwidth=1 here
    
        auto guard = sg::make_scoped_guard([w=getGLwidth()](){ setGLwidth(w); }); // capture current GLwidth in order to set it back
        setGLwidth(2); // sets GLwidth=2
    
        draw_line(t->a, t->b);
        draw_line(t->b, t->c);
        draw_line(t->c, t->a);
    
        setGLwidth(5);
    
        draw_point(t->a);
        draw_point(t->b);
        draw_point(t->c);
    
    }  // scoped_guard sets GLwidth back to 1 here
    

    以上内容在C++11上甚至不起作用。 更不用说试图以这种方式向lambda介绍州政府伤害了我的眼睛。

    使用 (C++11)

    在C++11中,必须执行以下操作:

    void DrawTriangle(Tria *t)
    {
        // GLwidth=1 here
    
        int previous_width = getGLwidth();  // explicitly capture current width 
        auto guard = sg::make_scoped_guard([=](){ setGLwidth(previous_width); }); // pass it to lambda in order to set it back
        setGLwidth(2); // sets GLwidth=2
    
        draw_line(t->a, t->b);
        draw_line(t->b, t->c);
        draw_line(t->c, t->a);
    
        setGLwidth(5);
    
        draw_point(t->a);
        draw_point(t->b);
        draw_point(t->c);
    
    }  // scoped_guard sets GLwidth back to 1 here
    

    • 这个 斯诺佩特要求

      • previous_width guard ,再次)以保持前一状态
    • 手工制作的 RAII class 要求

      • 警卫 )保持以前的状态。

    我认为这样的例子

    void some_function() {
        sg::scoped_guard([](){ cout << "this is printed last"; }
    
        cout << "this is printed first";
    }
    

    没有证据表明 .

    我希望有人能告诉我为什么我不能从中获得预期的收益 .

    内窥镜防护罩