您的位置:首页 > 编程语言

第2章 面向过程的编程风格

2012-04-07 00:15 316 查看
1.如果用户输入一个不合理的位置值,程序应该怎么处理呢?最极端的做法就是终止整个程序.标准函数库的exit()函数可派上用场.必须传一个值给exit(),此值将成为程序结束时的状态值.

if ( exit <=0 )

exit(-1);

为能使用exit(),必须先含入cstdlib头文件:

#include <cstdlib>

2.欲知道某个型别的最小最大值,可查询标准程序库中的numeric_limits class

#include <limits>

int max_int = numeric_limits<int> :: max();

double min_db1=numeric_limits<double>::min();

3.如果函数主体的最后一个语句不是return,那么最后一个语句之后便是该函数的隐性退出点.

4.将打算显示的vector传值方式传入display()中,这意味着每当我想进行显示操作时,vector内的所有元素都会被复制.这并不会造成错误.但是如果直接传入vector的地址,速度会更快.将vector的参数声明为reference,便可达到这个目的:

void display(const vector<int> &vec)

{

for ( int ix=0 ;ix<vec.size() ; ++ix)

{

cout<<vec[ix]<<' ';

cout<<endl;

}

}

我们声明了一个reference to const vector.这表明我们以传址的方式来传递vector,为的是避免复制操作,而不是为了要在函数之中对它进行修改.

如果我们愿意,也可以将vector以pointer形式传递.pointer参数和reference参数二者之间更重要的差异是,pointer可能指向某个实际对象.当我们提领pointer时,一定要先确定其值并非为0.至于reference则必定会代表某个对象,所以不须做此检查.

5.当我们调用一个函数时,会在内存中建立起一块特殊区域,称为"程序栈"这块特殊区域提供了每个函数参数的储存空间.它也提供函数所定义的每个对象的内存空间------我们将这些对象称为local object(局部对象).一旦函数完成,这块内存就会被释放掉,或者说是从程序堆栈中被pop出来.

除了static变量之外,函数内定义的对象,只存活于函数执行之际.如果将这些所谓局部对象的地址返回,会导致执行期错误.局部对象被置于程序栈上,当函数执行完后,这块区域的内容会被弃置,于是局部对象不复存在.一般而言,对根本不存在的对象进行寻址操作,是很不好的习惯.

不论以pointer或reference形式将elems返回,都不正确,国为elems在fibon_seq()执行完毕时已不复存在.如果将elems以传值方式返回,便不会产生任何问题;因为返回的乃是对象的复制品,它在函数之外依然存在.

6.不论local scope或file extent,对我们而言,都是由系统自动管理.第三种存储期形式称为dynamic extent(动态范围).其内存系由程序的自由空间配置而来,有时也称heap memory(堆内存).此种内存必须由程序员自行管理,其配置是通过new表达式来达成的,而其释放则经由delete表达式完成.

new Type(initial_value);

int *pi=new int;

由heap配置而来的对象,皆未经过初始化.new表达式的另一种形式允许我们指定初值.

pi=new int(1024);

7.如果要从heap中配置数组,可以这么写:

int *pia=new int[24];

C++没有提供任何语法让我们得以从heap配置数组的同时为其元素设定初值.

8.从heap配置而来的对象,被称为具有dynamic extent,因为它们是在执行期通过new表达式配置而来的,因此可以持续存活,直到以delete表达加以释放为止.下面的delete表达会释放pi所指的对象:

delete pi;

如果要删除数组中的所有对象,必须在数组指针和delete表达式之间,加上一个空的下标运算符:

delete [] pia;

注意,无需检验pi是否非零:

if( pi !=0 ) //多此一举----编译器会替我们检查

delete pi;

如果因为某种原因,程序员不想使用delete表达式,则由heap配置而来的对象就永远不会被释放.此称为memory leak(内存漏洞).

9.关于默认参数的提供,有两个不甚直观的规则:

第一个规则是,默认值的决议(resolve)操作由最右边开始进行.如果我们为某个参数提供了默认值,那么这个参数右侧的所有参数都必须也具有默认参数值才行.下面这样补发非法:

//错误:没有为vec提供默认值

void display(ostream &os=cout,const vector<int> & vec);

第二个规则是,默认值只能够指定一次,可以在函数声明处,亦可以在函数定义处,但不能够在两个地方都指定.

因为头文件可为函数带来更高的可见度.为了更高的可见度,我们决定将默认值置于函数声明处,而非定义处.

10.先前的做法中,fibon_elem()只须调用一个函数便可完成所有运算,如今必须运用3个函数.这成了它的缺点.如果执行执行效能不符合理想,只好再将3个函数重新组合为一个.然而C++还提供另一个解决办法,就是将这些函数声明为Inline.

将函数声明为inline,表示要求编译器在每个函数调用点上,将函数的内容展开.一般而言,最适合声明为inline的函数,和fibon_elem()以及is_size_ok()一样:体积小,常被调用,所从事的计算并不复杂.

inline函数的定义常常被置于头文件中.由于编译器必须在它被调用的时候加以展开,所以这个时候其定义必须是有效的.

11.function template以关键词template开场,其后紧接着以成对尖括号(<>)包围起来的一个或多个识别名称.

template<typename elemType>

void diaplay_message(const string &msg,const vector<elemType> &vec)

{

cout<<msg;

for(int ix=0;ix<vec.size();++ix)

{

elemType t=vec[ix];

cout<< t<< ' ';

}

}

如果函数具备多种实现方式,我们可将它重载,其每份实体提供的是相同的通用服务.如果我们希望让程序代码的主体不变,仅仅改变其中用到的数据型别,可以通过function template达成目的.

 当然,funtion template同时也可以是重载函数.

template <typename elemType> void display_message(const string &msg,const vector<elemType> &vec);

template <typename elemType> void display_message(const string &msg,const list<elemType> <);

12.函数指针,必须指明其所指向之函数的返回值类型及参数表

const vector<int>* (*seq_ptr)(int);

由函数指针寻址出来的函数,其调用方式和一般函数相同.也就是说以下式子:

const vector<int> *pseq=seq_ptr(pos);//seq_ptr(pos)失败会返回0

会间接调用seq_ptr所寻址的函数.我们不知道它所寻址的函数是哪一个.更明智的做法是,多付出一些检验操作,确定它是否真的寻址到某个函数:

if(!seq_ptr)

display_message("
4000
Internal Error:seq_ptr is set to null!");

我们可以给予函数指针初值.如果初值为0,表示并未指向任何函数:

const vector<int>* (*seq_ptr)(int) =0;

也可以拿某个函数的地址作为函数指针的初值.问题是,如何取得函数的地址呢?只要给予函数名称即可:

seq_ptr=pell_seq;

13.函数指针数组

const vector<int>* (*seq_array[])(int) = {
fibon_seq,lucas_seq,pell_seq,triang_seq,square_seq,pent_seq};

如果需要循环迭代的时候,可以通过一组辅助记忆的常量来进行索引操作,例如:

enum ns_type{ns_fibon,ns_lucas,ns_pell,ns_triang,ns_square,ns_pent};

关键enum之后是一个可有可无的识别名称.

我们可以使用对应的枚举成员作为数组的索引值.

seq_ptr=seq_array[ns_pell];

解释下:

ns_pell => 2

seq_array[2] => pell_seq

14.函数的定义只能有一份.不过倒是可以有许多份声明.我们不把函数的定义纳入头文件,因为同一程序的多个代码文件可能都会含入这个头文件.

"只定义一份"的规则有个例外:inline函数的定义.为了能够扩展inline函数的内容,在每个调用点上,编译器都取得其定义.这意谓着我们必须将inline函数的定义置于头文件,而不是把它放在各个不同的程序代码文件.

在file scope内定义的对象,如果可能被多个文件存取,就应该被声明于头文件中.因为如果我们没有在程序中声明某个对象,便无法加以取用.举个例子,如果seq_array函数指针数组被定义于file scope,我们可能会想要在NumSeq.h内提供它的声明.下面的尝试还是没有达到完全正确.

//这不十分正确

const int seq_cnt = 6;

const vector<int>* (*seq_array[seq_cnt])(int);

这并不很正确,因为它会解读为seq_array的定义而非声明.就像函数一样, 一个对象只能在程序中被定义一次.对象的定义,就像函数的定义一样,必须置于程序代码文件.只要在上述seq_array定义式前加上关键词extern,它便成为了一个声明:

extern const vector<int>* (*seq_array[seq_cnt])(int);//将全局变量的作用域扩展到本文件,如果是在定义的文件里面,则扩展到声明的地方

那为什么seq_cnt不需要加上关键词extern呢?

这是const object和inline函数一样,是"一次定义规则"下的例外.const object的定义只要一出文件外便不可见这意谓着我们可以在多个程序代码文件中加以定义,不会导致任何错误

(读者可能会疑惑,刚才讨论过的seq_array不也是一个const object吗?不,它不是,它是一个"指向const object"的指针,它本身并不是const object)

15.如果头文件中有int a=1;会出错.写成const int a=1;就不会.写成static int a=1;也可以,但后两者变量的地址不一样,如果在main中对a赋值不影响output中的a的值,它们各是一个a,各不干扰

16.不能在外部(指函数体外部)进行赋值,只能进行声明和定义,因为赋值语句执行不到.

17.

output.h文件内容

void output();

main.cpp文件内容

#include "output.h"

#include <iostream.h>//这里加了.h可以不写using namesapce std;

const int a=1;

int b=2;

static int c=3;

void main()

{

cout<< a << b<<endl;

output();

}

output.cpp文件内容

#include "output.h"

#include <iostream.h>

extern int a;

extern int b;

extern int c;

void output()

{

cout<<a<<b<<c<<endl;

}

会报错,a和c作用域不能扩展.

练习2.2

Pentagonal数列的求值公式是Pn=n*(3n-1)/2,借此产生1,5,12,22,35等元素值。试定义一个函数,利用上述公式,将产生的元素置入用户传入的vector之中,元素数目由用户指定。请检查元素数目的有效性。接下来撰写第二个函数,能够将所接获的vector的所有元素一一印出。此函数的第二参数接受一个字符串,表示储存于vector内的数列的类型,最后再写一个main(),测试上述两个函数。

#include <iostream>
#include <vector>
#include <string>

using namespace std;

bool calc_elements(int size,vector<int> &vec);
void display_elems(vector<int> &vec,const string &str,ostream &os=cout);

int main()
{
string title="Pentagonal Numeric Serias:";
vector<int> vec;

if(calc_elements(0,vec))
display_elems(vec,title);
if(calc_elements(8,vec))
display_elems(vec,title);
if(calc_elements(16,vec))
display_elems(vec,title);
if(calc_elements(8,vec))
display_elems(vec,title);
if(calc_elements(128,vec))
display_elems(vec,title);

return 0;
}

bool calc_elements(int size,vector<int> &vec)
{
if(size<=0 || size>=64)
{
cerr<<'\n'<<"Sorry.Invalide size:"<<size<<endl;
return false;
}
for(int i=vec.size()+1;i<=size;i++)
vec.push_back(i*(3*i-1)/2);
return true;
}

void display_elems(vector<int> &vec,const string &str,ostream &os)
{
os<<'\n'<<str<<"\n\t"<<endl;
for(int i=0;i<vec.size();i++)
{
cout<<vec[i]<<( (i+1)%10  ? ' ' : '\n');
}
os<<endl;
}
练习2.3

将练习2.2的Pentagonal数列求值函数分离为两个函数,其中之一为Inline,用来检验元素数目是否合理.如果的确合理,而且尚未被计算过,便执行第二个函数,执行实际的求值工作.

#include <iostream>
#include <vector>
#include <string>

using namespace std;

inline bool calc_elements(int size,vector<int> &vec);
void real_calc_elements(int size,vector<int> &vec);
void display_elems(vector<int> &vec,const string &str,ostream &os=cout);

int main()
{
string title="Pentagonal Numeric Serias:";
vector<int> vec;

if(calc_elements(0,vec))
display_elems(vec,title);
if(calc_elements(8,vec))
display_elems(vec,title);
if(calc_elements(16,vec))
display_elems(vec,title);
if(calc_elements(128,vec))
display_elems(vec,title);

return 0;
}
bool calc_elements(int size,vector<int> &vec)
{
if(size<=0 || size>=64)
{
cerr<<'\n'<<"Sorry.Invalide size:"<<size<<endl;
return false;
}
if(vec.size()<size)
real_calc_elements(size,vec);
return true;
}
void real_calc_elements(int size,vector<int> &vec)
{
for(int i=vec.size()+1;i<=size;i++)
{
vec.push_back(i*(3*i-1)/2);
}
}

void display_elems(vector<int> &vec,const string &str,ostream &os)
{
os<<'\n'<<str<<"\n\t"<<endl;
for(int i=0;i<vec.size();i++)
{
cout<<vec[i]<<( (i+1)%10  ? ' ' : '\n');
}
os<<endl;
}


练习2.4

写一个函数,以局部静态的vector存储Pentagonal数列元素.此函数返回一个const指针,指向该vector.如果vector的容量小于指定的元素数目,就扩充vector的容量.接下来再实现第二个函数,接受一个位置值并返回该位置上的元素.最后,撰写main()测试这些函数.

#include <iostream>
#include <vector>

using namespace std;

inline bool check_validity(int pos)
{
return (pos<=0 || pos>=64) ? false : true ;//return 后面整体可以不加,但其它地方要加
//如cout<< 1 ? "true" : "false"<<endl;
//这样的话就会出错.
}

const vector<int> * pentagonal_serias(int size)
{
static vector<int> vec;
if(check_validity(size) && size>vec.size())
{
for(int i=vec.size()+1;i<=size;i++)
{
vec.push_back(i*(3*i-1)/2);
}
}
return &vec;
}

bool pentagonal_elem(int pos,int &elem)
{
if(!check_validity(pos))
{
cerr<<"Invalidate position # "<<pos<<endl;
elem=0;
return false;
}
const vector<int>* pvec=pentagonal_serias(pos);
elem=(*pvec)[pos-1];
return true;
}

int main()
{
int elem;
if(pentagonal_elem(8,elem))
cout<<"element 8 is "<<elem<<'\n';
if(pentagonal_elem(88,elem))
cout<<"element 88 is "<<elem<<'\n';
if(pentagonal_elem(12,elem))
cout<<"element 12 is "<<elem<<'\n';
if(pentagonal_elem(64,elem))
cout<<"element 64 is "<<elem<<'\n';
return 0;
}


练习2.5

实现一个重载的max()函数,让它接受以下参数:(a)两个整数;(b)两个浮点数;(c)两个字符串;(d)一个整数vector;(e)一个浮点数vector;(f)一个字符串vector;(g)一个整数数组,以及一个表示数组大小的整数值;(h)一个浮点数数组,以及一个表示数组大小的整数值;(i)一个字符串数组,以及一个表示数组大小的整数值.最后,撰写main()测试这些函数.

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

inline int max(int t1,int t2)
{
return t1 > t2 ? t1 : t2;
}
inline float max(float t1,float t2)
{
return t1 > t2 ? t1 : t2;
}
inline string max(const string t1,const string t2)
{
return t1 > t2 ? t1 : t2;
}

inline int max(const vector<int> &vec)
{
return *max_element(vec.begin(),vec.end());
}

inline float max(const vector<float> &vec)
{
return *max_element(vec.begin(),vec.end());
}
inline string max(const vector<string> &vec)
{
return *max_element(vec.begin(),vec.end());
}
inline int max(const int *iarray,int size)
{
return *max_element(iarray,iarray+size);
}
inline float max(const float *farray,int size)
{
return *max_element(farray,farray+size);
}
inline string max(const string *strarray,int size)
{
return *max_element(strarray,strarray+size);
}

int main()
{
string sarray[]={"we","were","her","pride","of","ten"};
vector<string> svec(sarray,sarray+6);

int iarray[]={12,70,2,169,1,5,29};
vector<int> ivec(iarray,iarray+7);

float farray[]={2.5f,24.8f,18.7f,4.1f,23.9f};
vector<float> fvec(farray,farray+5);

int imax = max(max(ivec),max(iarray,7));
float fmax=max(max(fvec),max(farray,5));
string smax=max(max(svec),max(sarray,6));

cout<<"imax should be 169 -- found: "<<imax<<'\n';
cout<<"fmax should be 24.8 -- found: "<<fmax<<'\n';
cout<<"smax should be were -- found: "<<smax<<'\n';
return 0;
}


练习2.6
以template重新完成练习2.5,并对main()函数做适度的修改.

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

template <typename elemType>
inline elemType max(elemType t1,elemType t2)
{
return t1 > t2 ? t1 : t2;
}

template <typename elemType>
inline elemType max(const vector<elemType> &vec)
{
return *max_element(vec.begin(),vec.end());
}
template <typename elemType>
inline elemType max(const elemType *array,int size)
{
return *max_element(array,array+size);
}

int main()
{
string sarray[]={"we","were","her","pride","of","ten"};
vector<string> svec(sarray,sarray+6);

int iarray[]={12,70,2,169,1,5,29};
vector<int> ivec(iarray,iarray+7);

float farray[]={2.5f,24.8f,18.7f,4.1f,23.9f};
vector<float> fvec(farray,farray+5);

int imax = max(max(ivec),max(iarray,7));
float fmax=max(max(fvec),max(farray,5));
string smax=max(max(svec),max(sarray,6));

cout<<"imax should be 169 -- found: "<<imax<<'\n';
cout<<"fmax should be 24.8 -- found: "<<fmax<<'\n';
cout<<"smax should be were -- found: "<<smax<<'\n';

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息