首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在C++中检查不变量

在C++中检查不变量
EN

Stack Overflow用户
提问于 2011-01-19 21:58:10
回答 5查看 4K关注 0票数 16

在C++中是否有用于检查类不变式的既定模式?

理想情况下,将在每个公共成员函数的开始和结束时自动检查不变量。据我所知,带有类的C提供了特殊的beforeafter成员函数,但不幸的是,契约设计在当时还不是很流行,除了Bjarne之外没有人使用这个特性,所以他删除了它。

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

代码语言:javascript
复制
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 _
}

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

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

代码语言:javascript
复制
#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");
    }

    // ...
};

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

问题#2:你认为这种方法还有其他问题吗?你能改进它吗?

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

EN

回答 5

Stack Overflow用户

发布于 2011-01-19 22:27:10

理想情况下,将在每个公共成员函数的开始和结束时自动检查不变量

我认为这有点过头了;相反,我明智地检查了不变量。您的类的数据成员是private (对吗?),所以只有它的成员函数可以更改数据成员,从而使不变量无效。因此,您可以在更改了包含不变量的数据成员后立即检查该不变量。

票数 2
EN

Stack Overflow用户

发布于 2011-01-19 22:03:45

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

你通常可以使用宏和__LINE__创建一些东西,但是如果你只是选择了一个足够奇怪的名字,它应该已经可以了,因为你不应该(直接)在同一个作用域中有多个名字。这

代码语言:javascript
复制
class invariants_checker {};

template<class T>
class invariants_checker_impl : public invariants_checker {
public:
    invariants_checker_impl(T* that) : that_(that) {that_->check_invariants();}
    ~invariants_checker_impl()                     {that_->check_invariants();}
private:
    T* that_;
};

template<class T>
inline invariants_checker_impl<T> get_invariant_checker(T* that)
{return invariants_checker_impl<T>(that);}

#define CHECK_INVARIANTS const invariants_checker& 
   my_fancy_invariants_checker_object_ = get_invariant_checker(this)

这对我很有效。

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

我不确定它是否调用了UB技术。在实践中,这样做肯定是安全的-如果不是因为,在实践中,必须在相对于其他类成员的特定位置声明的类成员迟早会成为问题。

问题#2:您认为这种方法还存在其他问题吗?你能改进它吗?

见#2.拿一个中等大小的类,加上二十多个开发人员五年来的扩展和错误修复,我认为至少有一次把事情搞砸的可能性约为98%。

您可以通过向数据成员添加一个响亮的注释来在一定程度上缓解这种情况。还是这样。

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

我没有见过这种方法,但是考虑到您对before()after()的描述,我立即想到了同样的解决方案。

我想Stroustrup有一篇文章很多(~15?)几年前,他描述了一个重载operator->()以返回代理的处理类。然后,在其ctor和dtor中,它可以在忽略通过它调用的方法的情况下执行之前和之后的操作。

编辑:我看到Frerich添加了an answer fleshing this out。当然,除非您的类已经需要通过这样的句柄使用,否则这对您的类的用户来说是一个负担。(IOW:它不会工作。)

票数 1
EN

Stack Overflow用户

发布于 2011-01-19 22:32:40

#0:不能,但是使用宏可以稍微好一点(如果你同意的话)

#1:没有,但要视情况而定。您不能做任何会导致它在body之前被解除引用的事情(您的会这样做,但只是在此之前,所以它可以工作)。这意味着您可以存储它,但不能访问字段或虚拟函数。如果check_invariants()是虚的,那么调用它是不好的。我认为它可以在大多数实现中工作,但不能保证工作。

#2:我认为这会很单调乏味,而且不值得。这是我使用不变量检查的经验。我更喜欢单元测试。

#3:我看过了。如果你打算这么做,在我看来这是正确的方法。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4736044

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档