【问题标题】:What is good coding idiom to decompose a complicated expression?分解复杂表达式的好编码习惯是什么?
【发布时间】:2014-05-06 22:34:20
【问题描述】:

我的问题是关于编码风格和 C++ 中复杂表达式的分解。

我的程序有一个由类等组成的类组成的复杂层次结构。程序的许多对象都持有指向其他对象的指针或索引。有几个命名空间。正如我所说,它很复杂——我的意思是它是一个非常典型的 10,000 行 C++ 程序。

出现了规模问题。我的代码开始有很多不可读的表达式,比如p->a(b.c(r).d()).q(s)->f(g(h.i()))。 (就像我说的,它不可读。我看不下去了,而且是我写的!你可以看看它来抓住心情。)我试过重写这样的表达方式

{
    const I  ii = h.i();
    const G &gg = g(ii);
    const C &cc = b.c(r);
    // ..
    T *const qq = aa.q(s);
    qq->f(gg);
}

所有这些局部作用域的符号可以说使代码更具可读性,但我承认我并不关心整体风格。毕竟,一些局部符号是const & 引用,而另一些则代表数据的副本。如果我不小心遗漏了& 之一,从而调用了一些大对象的不必要副本怎么办?我的编译器几乎不会警告我。

此外,带有本地符号的版本是冗长的。

这两种解决方案都不适合。在 C++ 中是否存在一种更惯用、更不易出错的方式来分解不可读的表达式?

插图

如果对问题的一个最小的、可编译的说明有帮助,那么这里就是一个。

#include <iostream>

class A {
  private:
    int m1;
    int n1;
  public:
    int m() const { return m1; }
    int n() const { return n1; }
    A(const int m0, const int n0) : m1(m0), n1(n0) {}
};

class B {
  private:
    A a1;
  public:
    const A &a() const { return a1; }
    B(const A &a0) : a1(a0) {}
};

B f(int k) {
    return B(A(k, -k));
}

int main() {

    const B  &my_b = f(15);

    {
        // Here is a local scope in which to decompose an expression
        // like my_b.a().m() via intermediate symbols.
        const A  &aa = my_b.a();
        const int mm = aa.m();
        const int nn = aa.n();
        std::cout << "m == " << mm << ", n == " << nn << "\n";
    }

    return 0;

}

其他信息

我怀疑它是否与问题相关,但如果是:我的程序定义了几个模板,但目前不使用任何运行时多态性。

一个实际的例子

其中一位评论者请求我的代码提供一个实际示例。这里是:

bool Lgl::is_order_legal_for_movement(
    const Mb::Mapboard &mb, const size_t ix, Chains *const p_chns1
) {

    if (!mb.accepts_orders()) return false;
    const Basic_mapboard &bmb = mb.basic();
    if (!is_location_valid(bmb, ix, false)) return false;
    const Space            &x = mb.spc(ix);
    if (!x.p_unit()) return true;
    const size_t           cx = x.i_cst_occu();
    const Basic_space     &bx = x.basic();
    const Unit             &u = x.unit();
    const bool           army = u.is_army();
    const bool          fleet = u.is_fleet();
    const Order         order = u.order();
    const size_t           is = u.source();
    const Location         lt = u.target_loc();
    const size_t           it = lt.i_spc;
    const size_t           ct = lt.i_cst;
    // ...

    {   
        const Space        &s = mb.spc(is);
        const Basic_space &bs = s.basic();
        result = ( 
            (army_s && (
                bs.nbor_land(it) || count_chains_if(
                    Does_chain_include(ix), chns_s, false
                )   
            )) || (fleet_s && (
                // By rule, support for a fleet can name a coast,
                // but need not.
                ct == NO_CST
                ? is_nbor_sea_no_cst(bs, cs, it) 
                : is_nbor_sea       (bs, cs, lt) 
            ))  
        ) && is_nbor_no_cst(army, fleet, bx, cx, it);
    }   

    // ...

}

【问题讨论】:

  • 您的示例代码符号名称对提高问题的可读性没有多大帮助。并使用函数而不是表达式链。
  • 您的示例类可以轻松清理:只需使用 const 并删除访问器。突然间,一切都变得不那么杂乱了。除此之外,简洁而有意义的名称总是有帮助的。
  • 我真的希望您在实际代码中不要使用一个字母的变量名,因为这是示例行难以阅读的第一个原因。无论如何,我知道这不是答案,但这种表达方式可能是由于不尊重Law of Demeter - 这是非常不可取的。
  • 看来您可能正在链接 getter 和 setter 的深层序列。也许您将类滥用为简单的“数据组”?
  • @Ke 或者认为“使用 OOP”意味着必须隐藏所有成员。

标签: c++ idioms


【解决方案1】:

对于您的实际代码示例,我可以理解您为什么想让它更具可读性。我可能会这样重新编码:

if (army_s) {
    result = bs.nbor_land(it) || 
            count_chains_if(Does_chain_include(ix), chns_s, false);
} else if (fleet_s) {
    // By rule, support for a fleet can name a coast,
    // but need not.
    if (ct == NO_CST)
        result = is_nbor_sea_no_cst(bs, cs, it);
    else 
        result = is_nbor_sea(bs, cs, lt);
}
result &= is_nbor_no_cst(army, fleet, bx, cx, it);

它执行相同的逻辑,包括短路评估,但我认为更适合人类解释。我还遇到过使用这种风格生成更好的代码的编译器,而不是原始代码包含的非常复杂的复合行。

希望对您有所帮助。

【讨论】:

  • 您对位运算符&amp;= 的使用很有趣。我有时希望&amp;&amp;= 存在。是否使用&amp;= 标准样式? (十二或十五年前,我似乎记得一个将布尔值存储为整数的 GCC 版本,并且不保证 true 将始终为 1;所以我一直不愿意将按位运算符用于布尔逻辑。也许我的担心已经过时了?)
  • @thb:如果编译器不能保证 true 为 1,这有关系吗? 8|1 仍然正确。 8|4|16|1 ​​也是如此。等等。
猜你喜欢
  • 2019-09-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-30
  • 2021-06-25
  • 1970-01-01
相关资源
最近更新 更多