如何在 C++17 中将防御检查值分配给不可变类中的公共 const 变量?



在 Java 中断后回到 C++。尝试创建一个不可变的对象,并且在使用Java工作之后,公共const变量似乎是最明智的(如Java final(。

public:
const int A;

一切都很好,但是如果我想防御性地检查这个值,我该怎么做。 下面的代码对我来说似乎很奇怪,但与 Java 最终成员不同,在防御性检查(编译器错误(后,我似乎无法在构造函数中设置 A。

MyObj::MyObj(int a) : A(a) {
if (a < 0)
throw invalid_argument("must be positive");
}

A 的公共常量变量似乎比仅带有非 const int 的 getter 更清晰、更干净的解决方案,但如果这是不好的做法,则可以接受该或其他想法。

你的例子应该可以正常工作:

class MyObj {
public:
const int var;
MyObj(int var) : var(var) {
if (var < 0)
throw std::invalid_argument("must be positive");
}
};

(实时示例,或使用外联构造函数(

如果您打算MyObj始终是不可变的,则const成员是 可能没问题。如果您希望变量通常是不可变的,但仍有可能用赋值覆盖整个对象,那么最好使用带有 getter 的private变量:

class MyObj {
int var;
public:
MyObj(int var) : var(var) {
if (var < 0)
throw std::invalid_argument("must be positive");
}
int getVar() const { return var; }
};
// now allows
MyObj a(5);
MyObj b(10);
a = b;

编辑

显然,你想做的是这样的

MyObj(int var) {
if (var < 0)
throw std::invalid_argument("must be positive");
this->var = var;
}

这是不可能的;一旦const变量具有值,就无法更改。一旦构造函数的主体({}位(启动,const变量已经有一个值,尽管在这种情况下,该值是"未定义的",因为您没有设置它(并且编译器因此抛出错误(。

而且,这实际上没有意义。在检查之后或之前设置变量没有效率差异,并且任何外部观察者都无法看到差异,因为throw语句将展开堆栈,立即解构对象。

一般来说,N. Shead 的答案是常规做法 - 但您也可以考虑:

  1. 创建特定于域的类型并使用它们而不是常规基元。 例如,如果您的字段是电话号码,则具有一个类型TelephoneNumber,该类型在其构造函数(或工厂(中获取字符串,执行您想要的所有电话号码验证(并抛出无效(。 然后你写一些类似的东西:

    class Contact {
    const TelephoneNumber phone_;
    public:
    Contact(string phone) : phone_(phone) { ... }
    ...
    

    执行此操作时,将在初始化字段phone_时调用用于TelephoneNumber获取字符串参数的构造函数,并将进行验证。

    以这种方式使用特定于域的类型在网络上以"原始痴迷"作为"代码气味"的名称进行讨论。

    (这种方法IMO的问题在于,你几乎必须在任何地方使用它,从你的项目开始,否则你开始不得不到处都有显式(或隐式(转换,你的代码看起来像废话,你永远无法确定你的值是否已经过验证。如果您正在使用现有的代码库,则几乎不可能完全改造它,尽管您可能只是开始将其用于特别重要/无处不在的类型。

  2. 创建获取并返回某个值的验证方法,并执行必要的验证 - 无效时抛出,否则返回其参数。 下面是一个示例验证器:

    string ValidatePhoneNumber(string v) {
    <some kind of validation throwing on invalid...>
    return v;
    }
    

    并按如下方式使用它:

    class Contact {
    const string phone_;
    public:
    Contact(string phone) : phone_(ValidatePhoneNumber(phone)) { ... }
    

    当应用程序或库对特定于域的类型进行如此多的验证时,我已经看到使用此方法,以至于已经构建了这些特定于域的验证器方法的小型库并且代码阅读器习惯了它们。 我真的不会认为它是惯用的,但它确实有一个优点,即验证就在你可以看到它的开放环境中。

最新更新