30 December 2021

C++ 核心指南目录

P.9: Don’t waste time or space

理由

This is C++.

注意

为更好达到既定目的,可以消耗更多计算时间和计算空间。比如加快开发进度、保障资源安全、简化系统测试。

亚历山大·斯特潘诺夫曾说过:追求效率的另一个好处是,这个过程可以迫使你更深入地理解所要解决的问题。

“Another benefit of striving for efficiency is that the process forces you to understand the problem in more depth.” - Alex Stepanov

注:亚历山大·斯特潘诺夫(Alexander Stepanov),Alex Stepanov, STL(标准模板库)之父,并因此在 1995 年获得了第一届Dr. Dobb’s程序设计杰出奖,他曾任 Compaq 公司副总裁和首席科学家,AT&T 实验室副总裁和首席构架师, SGI 服务器和巨型计算机公司技术总监。Alexander 曾先后在 HP 实验室、AT&T 实验室、通用电气公司 R&D、Polytechnic University of New York 大学控制研究所和复杂自动化研究所工作和研究了 25 年。他的研究工作涉及程序设计、语言设计、存贮系统、路径计划算法、实时操作系统等。

例子

struct X {
    char ch;
    int i;
    string s;
    char ch2;

    X& operator=(const X& a) {
        ch = a.ch; i = a.i; s = a.s; ch2 = a.ch2;
        return *this;
    }
    X() {ch='\0'; i=0; s=""; ch2='\0';}
    X(const X& a) {ch = a.ch; i = a.i; s = a.s; ch2 = a.ch2;}
};

X waste(const char* p)
{
    if (!p) throw bad_exception{};
    int n = strlen(p);
    auto buf = new char[n];
    if (!buf) throw bad_alloc{};
    for (int i = 0; i < n; ++i)
        buf[i] = p[i];     // ... manipulate buffer ...
    X x;
    x.ch = 'a';
    x.s = string(n, '\0');  // give x.s space for *p
    for (gsl::index i = 0; i < x.s.size(); ++i)
        x.s[i] = buf[i];    // copy buf into x.s
    delete[] buf;
    return x;
}

void driver()
{
    X x = waste("Typical argument");
    cout << x.s << endl;
}
int main()
{
    driver();
    return 0;
}
Typical argument

以上例子以夸张的手法,展现了计算资源如何被无情的浪费。 X 的内部数据布局至少浪费了 6 字节(每个 char 处浪费 3 字节)。 buf 多余的创建和删除操作。

void lower(zstring s)
{
    for (int i = 0; i < strlen(s); ++i) s[i] = tolower(s[i]);
}

以上代码中,i < strlen(s)每次循环都会执行。因为只是改了字符串字符的大小写,并不改变字符串长度,因此没必要重复计算 s 的长度。因此可以这样改:

void lower(zstring s)
{
    int len = strlen(s);
    for (int i = 0; i < len; ++i) s[i] = tolower(s[i]);
}

注意

个别例子中,我们很容易发现浪费资源的情况,也很容易修复。但是,如果整个代码库中都分散着浪费资源的情况,我们就很难及时发现并修复了。所以,本条指南期望能够在使用C++前发现大部分浪费资源的情况。然后,通过检查算法和需求,发现更深层次的资源浪费的情况。不过,这些超出了本指南的范围。

强化

  • 检查标记返回值未被使用的,某些用户自定义的,非默认的后缀操作,如 operator++operator-- 函数。选择使用前缀形式。