C++基础1. 1 基本概念
1.1 面向对象的程序设计思想
面向对象程序设计的本质是把数据和处理数据的过程当成一个整体对象。
C++ 充分支持面向对象的程序设计。面向对象 程序设计的实现需要封装和数据隐藏技术,
需要继承和多态技术。
(1)封装和数据隐藏:
C++ 通过建立用户定义类型(类)支持封装性和数据隐藏。完好定义的类一旦建立,就可以
看成是完全封装的实体,可以作为一个整体单元使用。类的实际内部工作应当隐藏起来,使用完
好定义的类的用户不需要知道类是如何工作的,只要知道如何使用它就行。
(2)继承和重用:
C++ 采用继承支持重用的思想,程序可以在扩展现有类型的基础上,声明新类型。新子类是
从现有类型派生来的,称为派生类。
(3)多态性:
4. 3、面向对象的实现:
面向对象的实现就是具体的编码阶段,主要工作是:
•选择一种合适的面向对象的变成语言,如 C++ 、 Java 等。
•用选顶的语言编码实现详细设计步骤所的的公式、图表、说明和规则等对软件系统个对象类的详
尽描述。
•将编写好的各个类代码模块根据类的相互关系集成。
•作好测试工作。
1.3 C++ 程序的开发过程
程序员开发 C++ 程序的过程通常由四步组成,即编辑、编译、连接、运行。
程序员首先在集成开发环境中编辑源程序,或者在其他编辑器中输入源程序,然后在集成开发环
境汇总启动编译程序( compile )编译后,生成一个目标文件,这个文件通常以 .obj 为文件扩展
名,该目标文件为源程序的目标代码,即机器语言程序。最后由连接程序( link )将目标程序连
接后,形成可执行程序( .exe )。
整个开发过程如下所示:
7. returnx+y;
}
例 2 : void message()
{
cout<<”this is a messagen”;
}
•函数原型
函数原型又称为函数声明。和变量在使用之前必须先定义相似,函数在使用之前也必须先定
义或声明。在 C++ 中,函数的声明原则如下:
如果一个函数定义在先,调用在后,调用前可以不必声明;如果一个函数定义在后,调用在
先,调用前必须声明。
根据这个原则,凡是被调用函数都在调用函数之前定义时,可以对函数不加声明。但是,这
样做要在程序中安排函数的顺序上费一些精力,在复杂的调用中,一定要考虑好谁先谁后,否则
将发生错误。因此,避免确定函数定义的顺序,并且使得程序设计上逻辑结构的清楚,常常将主
函数放在程序头,这样就需要对被调用的函数进行说明。
15. void fun(int =1,int =3);
调用该函数时, fun(7) 到底调用的是哪一个呢?这时就引起了调用的二义性。
⑷默认参数可以将一系列简单的重载函数合成为一个,从而优化编程。
例如: void para(int a=1,int b=2,int c=3);
调用时,可以带一个参数 para(1) ,可以带两个参数 para(2,4) ,可以带三个参数
para(10,20,30) 。
也可以不带参数 para() 。这样可以消除简单的函数重载。
⑸参数的默认值可以是全局变量、全局常量,甚至可以是一个函数。
4.2 函数的调用机制
C++ 的函数调用过程,就是栈空间操作的过程。
栈是一种数据结构,这种数据结构遵循一个原则:先进后出。即先进栈的数据后出,而后进
栈的数据先出。函数调用实际上是进行程序的跳转,在转去执行函数之前,应把现场保护起来,
以备函数执行完毕还回到刚才跳转的地方,接着执行后继程序。所以函数调用时, C++ 会做保护
现场的工作:
⑴建立被调函数的栈空间。
20. 当主函数调用 func1() 时, func1() 着手保护户调用函数的地址等数据,分配两个形参的空间,
将主函数的实参传递过来。则 func1() 中的两个形参分别为 6 和 12 。
Func1() 的栈区和 main() 的栈区是互相独立的。在 func1() 中不能访问 main() 中的局部变量 a
和 b 。通过参数传递,使得 func1() 中的形参赋有 main() 函数中的实参值。 Func1() 可以修改其变
量 aa 和 bb ,但是始终不能影响 main() 中的 a 和 b 。这也是 C++ 中的传值特性。
在函数 func1() 中,定义了一个局部变量 n, 并一该变量作为实参调用函数 func2() 。同样,
func2() 建立了自己的栈空间,保护 func1() 的返回地址,获得参数值,随后运行它自己的函数体语
句。
当 func2() 执行完毕,将从栈中将 func1() 的返回地址弹出,接着执行 func1() 的其他语句,当
func1() 执行完毕,又从栈中返回 main() 的返回地址,接着执行 main() 中的其他语句。 Main() 函数
执行完毕,返回系统地址。这个应用程序就这样执行完毕。
栈在系统中不是用之不竭的资源,每次调用一个函数,所建立的栈空间都比上一个函数的栈
空间要小。所以一层一层的调用,会出现栈空间不够的情况,这就会出现栈溢出的问题。解决的
方法,是在连接之前通过设置栈空间的大小来改善栈空间有限的局限。
4.3 内联函数、递归函数和函数重载
1、内联函数
内联函数也称内嵌函数。引入内联函数的目的是为了解决程序中函数调用的效率问题。前面
我们讲过,函数的引入使得编程者只关心函数的功能和使用方法,而不必关心函数功能的具体实
现;另外,函数的引入可以减少程序的代码,实现程序代码和数据的共享。但是函数调用也会带
来降低效率的问题,因为调用函数实际上将程序执行顺序转移到函数所存放的内存地址处,将函
22. inlineint curb(int x)
{
return x*x*x;
}
void main()
{
for(int I=1;I<=100;I++)
cout<<I<<”*”<<I<<”*”<<I<<curb(i)<<endl;
}
该程序中,函数 curb() 是内联函数,该函数在编译时被其代码替代,而不象普通函数那样是
在运行时被调用。
⑵几点注意:
•在内联函数中不允许用循环语句和开关语句。
26. 例6:以下函数 poly 是用递归方法计算 x 的 n 阶勒让德多项式的值。已有调用语句“ p(n,x)”; 编写
poly 函数。递归公式如下:
poly n (x)=1 当 n=0 时;
poly n (x)=x 当 n=1 时 ;
poly n (x)=((2n-1)*x*poly n-1 (x)-(n-1)*poly n-2 (x))/n 当 n>1 时
程序如下:
#include <iostream.h>
double p(intn,double x);
void main()
{
int m;
double xx;
30. }
该程序中, main() 函数调用了相同名字 minue 的两个函数,前边一个 minue() 函数对应的是
两个 int 型数的差的函数的实现,而后边一个 minue() 函数对应的是两个 double 型数差的函数实现,
这就是函数的重载。
•几点注意:
⑴重载函数不能仅仅是返回类型不同,还必须在参数类型、参数个数和参数顺序上有所不同。
例如,下面的声明有问题:
void f(double);
double f(double);
编译器无法区分函数调用 f(3.5) 是指上述哪个重载函数。
⑵不能用 typedef 定义的类型名来区分重载函数声明中的参数。因为用 typedef 定义的类型只是已
有类型的别名而已。
⑶重载函数描述的应该是相同功能,而只是数据类型不同的代码,否则会影响程序的可读性。
31. ⑷当一个函数调用找不到严格相匹配的重载函数时,会将参数进行类型转化,寻求其他匹配。但
这时容易出现二义性。
如: void print( long);
void print(double);
函数调用 print(3) 可以转化为 long 型,也可以转化为 double ,这时就分辨不出该和哪个函数
匹配了。从而出现二义性。
例8:编写一个包含三个重载函数的程序,重载函数名为 display() 。第一个函数输出一个
double 型值,前面用字符串”A double:”引导;第二个函数输出一个 int 值,前面用字符串”A int:”引
导;第三个函数输出 char 字符,前面用字符串”A char:”引导。在主函数中,分别用
double,float,char 和 short 型变量去调用 display() 函数,并对结果做简要说明。
程序如下:
#include <iostream.h>
void display(double);
void display(int);
void display(char);
33. cout<<” A int :”<<a<<endl;
}
void display(char a);
{
cout<<” A char :”<<a<<endl;
}
程序运行结果:
A int:12
A double:12.5
A char: A
第一行的结果调用了 void display(int), 第二行输出和 void display(double) 匹配,第三行输出和
void display(char) 匹配。
例9:分析下列程序的运行结果:
42. void func1();
void func2();
void func3();
inti=5;
void main()
{
i=20;
func1();
cout<<”main():i=”<<i<<endl;
func2();
cout<<”main():i=”<<i<<endl;
func3();
cout<<”main():i=”<<i<<endl'