23 December 2021

C++ 核心指南目录

如果程序里有很难检查到的错误的话,可能会在特定的条件下,导致莫明其妙的错误。因此要尽量在编译时和运行时检测到这些错误。当然,错误检测是需要代价的,如计算资源、计算时间等。

例子:

#include <iostream>
using namespace std;
void f(int* p) {
    cout << p[50];
}

void g(int n)
{
    // bad: the number of elements is not passed to f()
    f(new int[n]);
}
int main()
{
    g(3);
    return 0;
}
-2043739824

以上代码打印出了一个莫明其妙的数字。因为数组大小没有传给 f ,在 f 中又可以访问 p 的任意元素,超出了数组的范围。

#include <iostream>
using namespace std;
void f2(int* p, int n) {
    cout << p[n];
}

void g2(int n)
{
    f2(new int[n], 10);  // bad: a wrong number of elements can be passed to f()
}
int main()
{
    g2(3);
    return 0;
}
1647575376

上面代码虽然添加了数组大小作为参数,但是这个参数的值可以是任何数字,一旦数值给错,就访问越界了,所以也不安全。

#include <iostream>
#include <memory>
using namespace std;
void f3(unique_ptr<int[]> p, int n) {
    cout << p[n];
}

void g3(int n)
{
    f3(make_unique<int[]>(n), 10);    // bad: pass ownership and size separately
}
int main()
{
    g3(3);
    return 0;
}
-1123024560

独占指针( unique_ptr )可以用来传递指针所有权。但是上面还是单独传递了数组大小参数给 f3 。错误的参数,依然会导致越界访问数组。

#include <iostream>
#include <memory>
#include <vector>
#include <gsl/gsl>
using namespace std;
void f4(vector<int>& v) {
    cout << v[5];
}
void f5(gsl::span<int> v) {
    cout << v[5];
}

void g3(int n)
{
    vector<int> v(n);
    f4(v);                     // pass a reference, retain ownership
    f5(gsl::span<int>{v});          // pass a view, retain ownership
}
int main()
{
    g3(9);
    return 0;
}
0

以上代码,通过引用和 span 视图的方式传递参数,数组大小是 span 对象自身包含的数据,可以在运行时检查是否越界访问了。

以下代码使用 vector 对象能够传递所有权,并且同时保留了大小信息:

#include <iostream>
#include <vector>
#include <gsl/gsl>
using namespace std;
using namespace gsl;
vector<int> f5(int n)       // OK: move
{
    vector<int> v(n);
    // ... initialize v ...
    return v;
}

unique_ptr<int[]> f6(int n)  // bad: loses n
{
    auto p = make_unique<int[]>(n);
    // ... initialize *p ...
    return p;
}

owner<int*> f7(int n)        // bad: loses n and we might forget to delete
{
    owner<int*> p = new int[n];
    // ... initialize *p ...
    return p;
}

int main()
{
    auto v5 = f5(5);
    cout << v5.size() << endl;
    cout << v5.at(4) << endl;
    auto v6 = f6(5);
    cout << v6[6] << endl;   // out of range
    auto v7 = f7(5);
    cout << v7[6] << endl;   // out of range
    delete v7;
    return 0;
}
5
0
-1355022000
-1554522460