用C++检查不变量

fre*_*low 15 c++ templates design-by-contract invariants object-lifetime

是否有任何已建立的模式来检查C++中的类不变量?

理想情况下,将在每个公共成员函数的开头和结尾自动检查不变量.据我所知,带有类的C提供了特殊功能beforeafter成员功能,但不幸的是,合同设计当时并不普及,除了Bjarne之外没有人使用该功能,所以他删除了它.

当然,check_invariants()在每个公共成员函数的开头和结尾手动插入调用是繁琐且容易出错的.由于RAII是处理异常的首选武器,我提出了以下方案,将不变性检查器定义为第一个局部变量,并且不变性检查器在构造和销毁时检查不变量:

template <typename T>
class invariants_checker
{
    const T* p;

public:

    invariants_checker(const T* p) : p(p)
    {
        p->check_invariants();
    }

    ~invariants_checker()
    {
        p->check_invariants();
    }
};

void Foo::bar()
{
    // class invariants checked by construction of _
    invariants_checker<Foo> _(this);

    // ... mutate the object

    // class invariants checked by destruction of _
}
Run Code Online (Sandbox Code Playgroud)

问题#0:我想没有办法声明一个未命名的局部变量?:)

我们仍然需要check_invariants()Foo构造函数的末尾和Foo析构函数的开头手动调用.但是,许多构造函数体和析构函数体都是空的.在那种情况下,我们可以使用a invariants_checker作为最后一个成员吗?

#include <string>
#include <stdexcept>

class Foo
{
    std::string str;
    std::string::size_type cached_length;
    invariants_checker<Foo> _;

public:

    Foo(const std::string& str)
    : str(str), cached_length(str.length()), _(this) {}

    void check_invariants() const
    {
        if (str.length() != cached_length)
            throw std::logic_error("wrong cached length");
    }

    // ...
};
Run Code Online (Sandbox Code Playgroud)

问题#1:传递this给通过该指针invariants_checker立即调用的构造函数是否有效check_invariants,即使该Foo对象仍在构建中?

问题2:你认为这种方法还有其他问题吗?你能提高吗?

问题3:这种方法是新的还是众所周知的?有更好的解决方案吗?

Fre*_*abe 11

答案#0:您可以拥有未命名的局部变量,但是您可以放弃对对象生命周期的控制 - 而对象的整个点是因为您有一个好主意,当它超出范围时.您可以使用

void Foo::bar()
{
    invariants_checker<Foo>(this); // goes out of scope at the semicolon
    new invariants_checker<Foo>(this); // the constructed object is never destructed
    // ...
}
Run Code Online (Sandbox Code Playgroud)

但你不想要的.

答案#1:不,我相信它无效.引用的对象this仅在构造函数完成时完全构造(因此开始存在).你在这里玩一个危险的游戏.

回答#2:这种方法并不新鲜,一个简单的谷歌查询,例如"检查不变量C++模板"将在这个主题上产生大量的点击.特别是,如果您不介意重载->运算符,可以进一步改进此解决方案,如下所示:

template <typename T>
class invariants_checker {
public:
  class ProxyObject {
  public:
    ProxyObject(T* x) : m(x) { m->check_invariants(); }
    ~ProxyObject() { m->check_invariants(); }
    T* operator->() { return m; }
    const T* operator->() const { return m; }
  private:
    T* m;
  };

invariants_checker(T* x) : m(x) { }

ProxyObject operator->() { return m; } 
const ProxyObject operator->() const { return m; }

private:
   T* m;
};
Run Code Online (Sandbox Code Playgroud)

这个想法是,在成员函数调用的持续时间内,您创建一个匿名代理对象,该对象在其构造函数和析构函数中执行检查.你可以像这样使用上面的模板:

void f() {
  Foo f;
  invariants_checker<Foo> g( &f );
  g->bar(); // this constructs and destructs the ProxyObject, which does the checking
}
Run Code Online (Sandbox Code Playgroud)