C++: Essential C++ 读书笔记:面向过程编程:使用局部静态对象.。
背景:先看下面一个案例
vector<int> fibon_seq(int size)
{
if (size <= 0 || size >= 1024) {
cout << "Waring: fibon_seq():" << size int
<< "not supported --reseting to 8" << endl;
size = 8;
}
vector<int> elems(size);
for (int ix = 0; ix < size; ++ix) {
if (ix == 0 || ix ==1)
elems[ix] = 1;
else elems[ix] = elems[ix-1] + elems[ix-2];
}
return elems;
}
针对这个案例,我们从两个 方面讨论
1:返回值:以 pointer/ reference (传址方式),传值方式返回。
2:将 elems 定义成局部静态对象。
1:传址 方式返回函数内定义的对象------不可取
除了一个必要的例外(意指 static 对象),函数内定义的对象,只存在于函数执行 期间。
如果将这些所谓的局部对象的地址返回,会导致运行时错误。
因为:函数是暂时位于程序堆栈(内存的一块特殊区域)之上。局部对象就是放在这块区域,当函数执行完毕,这块区域的内容会被弃置,局部对象也不复存在,一般而言,对根本不存在的对象进行寻址操作,是很不好的一个习惯。
上述例子中:
不论我们是以pointer或者reference形式将 elems返回,都是不正确的,因为elems在 fibon_seq()执行 完毕时已不复存在,将一个不存在的对象,如果做寻址操作,是会出现意想不到异常的。
但是:
如果,我们以传值的方式将elems 返回,就不会出现任何问题,因为传值这种方式,它返回的是对象的副本,在函数之外,它仍然存在。
- 对象在程序内的存活区域称为该对象的 scope(作用域)。 我们说 size和 elems在 fibon_seq()执行,都会为elems 分配内存,每当 fibon_seq() 结束便会加以释放。我们称此对象具有局部性范围(local extent),函数参数也具有局部性范围。
- 对象如果函数之外声明,具有所谓的 file scope 。对象如果用用 file scope,从其声明点至文件末尾都是可见的。 file scope内的对象也具备 所谓的 static extent (静态存储区)。 意即该对象的内存在 main()开始执行之前便已经分配好,可以一直存在至程序结束。
2:传 址方式可取之处
- 当我们以 by reference方式将对象作为函数参数传入时,对象本身不会复制出另一份 ------复制的是对象的地址。所以,在函数中对该对象的任何操作,都相当于是对传入的对象进行间接操作。
- 将参数声明为 reference的理由之一是:希望直接对传入的对象进行修改,这个理由极为重要。
- 将参数作为 reference的理由之二是:降低复制大型对象的额外负担,这个理由相比起来不那么重要,因为只是堆程序而言不过就是效率问题。
那么by pointer 和by reference的区别是什么了?
1:共同点:都可以直接对传入的对象进行修改
2:不同点:
- by reference 是引用代表某个对象,一旦代表某个对象,就不能代表其他对象,C++规定,引用代表的对象必须从一而终。 (reference,必定会代表某个对象,在寻址时,不必做检查)
- by pointer : 是指向某个对象,它可以随时随地指向任何不同对象,也 可以不指向某个实际对象(所以当我们提领某个 pointer时,一定要检查其值是否为0)
3:使用局部静态对象
在 fibon_seq()函数每次没调用时,会以一个vectror储存计算出来的元素,然后返回,这里话费了一些不必要的功夫。
我们需要的是一个vector,从 fibon_seq()的某次调用到下一次调用,这中间唯一会改变的只是用户指定的元素个数而已,不需要新的每次调用都去构造新vector对象。
显然在函数内部声明局部(local)vector对象并不能解决上述问题,因为局部对象会在每次调用函数时建立,并在函数结束的同时被弃置。
- 如果将vector对象定义于 file scope中,又过于冒险,是的。为了节省函数间的通信问题而将对象定义于 file scope内,这永远是一种冒险,通常,file scope对象会打乱不同函数之间的独立性,使他们难以理解
- 这个时候 局部静态变量就起作用了,和 局部非静态变量不同的是:局部静态对象所处的内存空间在:静态区,即使在不同函数调用过程中,依然持续存在。elems的内容不再向以前一样地在 fibon_seq()每次被调用时就被破坏后又被重建。这也意味着,我们可以安全的将 elem以 地址的方式返回。
const vector<int>* fibon_seq(int size)
{
const int max_size = 1024;
static vector<int> elems;
if (size <=0 || size > max_size)
{
cout<< "fibon_seq(): oops: invialid size: "
<< size << " --cannot fulfill request";
return 0;
}
// 如果size 等于或小于 elems.size(), 就不必重新计算
for(int ix = elems.size(); ix < size; ix++) {
if (ix ==0 || ix == 1) {
elems.push_back(1);
} else{
elems.push_back(elems[ix-1] + elems[ix +2]);
}
}
return &elems;
}