构造函数初始化列表中的约束检查

Seb*_*ach 9 c++ initialization constraints initialization-list

这种情况与如何对构造函数的参数进行约束有关,但它略有不同.

您想要初始化非默认可构造成员,但需要在构造之前检查约束.

例:

(请注意,这只是一个例子.在这种特定情况下是否应该使用无符号整数是可以讨论的,但问题实际上是关于你想检查构造函数的一般情况)

你有以下课程:

class Buffer {
public:
    Buffer() = delete;
    Buffer(int size) noexcept;
};
....


class RenderTarget {
public:
    ....
private:
    int width_, height_;
    Buffer surface_;
};
Run Code Online (Sandbox Code Playgroud)

构造函数必须检查整数参数的有效性:

RenderTarget::RenderTarget(int width, int height) :
    width_(width), height_(height),
    surface_(width_*height)
{
    if (width_<0 || height_<0)
        throw std::logic_error("Crizzle id boom shackalack");
}
Run Code Online (Sandbox Code Playgroud)

注意如何Buffer没有默认构造函数,而真正的构造函数是noexcept,即没有办法捕获错误.

当整数参数为负数时,已经有一个surface_已经加了.使用约束值之前进行约束检查会更好.可能吗?

Seb*_*ach 17

命名构造函数

您可以使用所谓的命名构造函数(另请参阅http://www.parashift.com/c++-faq/named-ctor-idiom.html),并创建构造函数private:

class RenderTarget {
private:
    RenderTarget (int w, int h) :
        width_(w), height_(h), buffer_(w*h) 
    {
        // NOTE: Error checking completely removed.
    }

public:
    static RenderTarget create(int width, int height) {
        // Constraint Checking
        if (width<0 || height<0)
            throw std::logic_error("Crizzle id boom shackalack");

        return RenderTarget(width, height);
    }
Run Code Online (Sandbox Code Playgroud)

如果您有多个可能不明确使用的构造函数,则命名构造函数很有用,例如Temperature < - Celsius | 华氏温度| 开尔文距离< - 米| 院子里| Cubit | 公里|.公里 ....

否则,(个人意见)他们强加意想不到的抽象和分心,应该避免.

三元算子和 throw

C++允许在[expr.cond]中使用-expressionsthrow作为三元运算符(-operator)的一个或两个操作数?::

RenderTarget(int w, int h) :
    width_(w<0 ? throw std::logic_error("Crizzle id boom shackalack") : w),
    height_(h<0 ? throw std::logic_error("Crizzle id boom shackalack") : h),
    surface_(w*h)
{}
Run Code Online (Sandbox Code Playgroud)

如果你不存储参数,你?:当然也可以在表达式中使用:

RenderTarget(int w, int h) :
    surface_(
       (w<0 ? throw std::logic_error("Crizzle id boom shackalack") : w)
     * (h<0 ? throw std::logic_error("Crizzle id boom shackalack") : h)
    )
{}
Run Code Online (Sandbox Code Playgroud)

或者将前置条件检查合并到一个操作数中:

RenderTarget(int w, int h) :
    surface_(
       (w<0||h<0) ? throw std::logic_error("Crizzle id boom shackalack") :
       w * h
    )
{}
Run Code Online (Sandbox Code Playgroud)

使用?:带有throw-expression 内联的-operator 对于基本约束检查非常好,并且避免必须回退到使用默认构造函数(如果有的话),然后在构造函数体内进行"实际初始化".

对于更复杂的场景,这可能会变得有点笨拙.

静态私人会员

当然,可以使用两全其美的优点:

private:
    static bool check_preconditions(int width, int height) {
        if (width<0 || height<0)
            return false;
        return true;
    }
public:
    RenderTarget(int w, int h) :
        surface_(
           check_preconditions(w,h) ?
           w*h :
           throw std::logic_error("Crizzle id boom shackalack")
        )
    {}
Run Code Online (Sandbox Code Playgroud)

...或者您需要为预处理检查所需的任何成员编写静态函数:

private:
    static Buffer create_surface(int width, int height) {
        if (width<0 || height<0)
            throw std::logic_error("Crizzle id boom shackalack")
        return Buffer(width*height);
    }

public:
    RenderTarget(int w, int h) :
      surface_(create_surface(w, h))
    {}
Run Code Online (Sandbox Code Playgroud)

这很好,因为您拥有完整的C++ - 用于约束检查的机器,例如可以轻松添加日志记录.它可以很好地扩展,但对于简单的场景来说不那么方便.