计算机项目部第一次培训总结
- cb的分步调试
- 递归思想
- 数组及字符串回顾
- 指针
- 面向对象编程基本概念
此次部门培训的内容是codeblocks的分步调试的相关操作,探究递归的思想,回顾数组与字符串知识。开始初步了解指针及相关操作,最后的时间引入了面向对象编程的思想,引申出类与对象的基本概念。
*计项部的小狗熊们要认真看哟 *
一、cb的分布调试
一串小代码:
//输入一个数,输出这个数到1之间的数
#include <iostream>
#include <stdio.h>
using namespace std;
void test(int n){
if(n==0)
return ;
cout<<n<<" ";
test(n-1);
}
int main(void){
int n;
cin>>n;
test(n);
return 0;
}
调试
快捷栏红圈标示,如果没有,在view下找到Toolbars,把那些东西勾选对号即可。
首先把光标放在int n上,然后按F4(因为版本的不同,有些同学的cb是ctrl shift F7),或者
表示 run to cursor
然后按F7即可单步执行,或者
表示next line(下一步)
这里需要输入n的值
下面是一个test函数,如果再按F7,就会直接到return 0,如果想看test函数的执行过程,两个方法:
①设置断点(F5)
为了方便查看相关变量的变化,我们可以打开watches,下图中的红圈,点击选择watches即可。
②step into ,step out
这个step into可以跳入函数,如果想跳出则点击step out就行了。
退出调试
二、递归思想
1、基本思想
所谓递归,就是有去有回。
递归的基本思想,是把规模较大的一个问题,分解成规模较小的多个子问题去解决,而每一个子问题又可以继续拆分成多个更小的子问题。
最重要的一点就是假设子问题已经解决了,现在要基于已经解决的子问题来解决当前问题;或者说,必须先解决子问题,再基于子问题来解决当前问题。
或者可以这么理解:递归解决的是有依赖顺序关系的多个问题。
我们假设一个抽象问题有两个时间点要素:开始处理,结束处理。
那么递归处理的顺序就是,先开始处理的问题,最后才能结束处理。
假设如下问题的依赖关系:
【A】----依赖---->【B】----依赖---->【C】
我们的终极目的是要解决问题A,
那么三个问题的处理顺序如下:
开始处理问题A;
由于A依赖B,因此开始处理问题B;
由于B依赖C,开始处理问题C;
结束处理问题C;
结束处理问题B;
结束处理问题A。
2、广义递归
从函数调用看广义递归
对于软件来说,函数的调用关系就是一个广义递归的过程,如下,
func_A()
{
func_B();
}
func_B()
{
func_C();
}
func_C()
{
/
}
调用函数A;
调用函数B;
调用函数C;
函数C返回;
函数B返回;
函数A返回;
3、狭义递归
有一种特例,就是处理问题A/B/C的方法是一样的,这就是产生了狭义的“递归函数“,即函数内又调用函数自身。
从上述分析看,递归对问题的处理顺序,是遵循了先入后出(也就是先开始的问题最后结束)的规律。
先入后出?栈!
没错,广义递归问题的处理,需要用栈来解决。经典的例子就是函数调用,就是依靠栈来实现的。
4、递归与栈
- 在入栈之前,通常需要完成三件事。
1、将所有的实参、返回地址等信息传递给被调函数保存。
2、为被调函数的局部变量分配存储区。
3、将控制转移到北调函数入口。
-当 一个函数完成之后会进行出栈操作,出栈之前同样要完成三件事。
1、保存被调函数的计算结果。
2、释放被调函数的数据区。
3、依照被调函数保存的返回地址将控制转移到调用函数。
测试代码
#include <stdio.h>
void recurrence(int num)
{
if ( num < 0 )
return;
printf("%d\n", num);
recurrence(num - 1);
printf("%d\n", num);
}
int main(void)
{
recurrence(5); return 0;
}
图解
三、数据及字符串回顾
题目检测:
(复习相关知识后)
有一个3*4矩阵,请你编写程序早找出每一行中最大值一次输出来
(用函数和指针相关知识解决)
四、神奇的指针
1、对指针变量的类型说明包括三个内容:
(1)指针类型说明,即定义变量为一个指针变量;
(2)指针变量名;
(3)变量值(指针)所指向的变量的数据类型。
其一般形式为: 类型说明符 变量名;
2、其中,表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指针变量所指向的变量的数据类型。
例如: int p1;表示p1是一个指针变量,它的值是某个整型变量的地址。 或者说p1指向一个整型变量。至于p1究竟指向哪一个整型变量, 应由向p1赋予的地址来决定。
应该注意的是,一个指针变量只能指向同类型的变量(基类型),如P3 只能指向浮点变量,不能时而指向一个浮点变量, 时而又指向一个字符变量
3、取地址运算符&
取地址运算符&是单目运算符,其结合性为自右至左,其功能是取变量的地址。在scanf函数及前面介绍指针变量赋值中,我们已经了解并使用了&运算符。
4、取内容运算符
取内容运算符是单目运算符,其结合性为自右至左,用来表示指针变量所指的变量。在运算符之后跟的变量必须是指针变量。需要注意的是指针运算符和指针变量说明中的指针说明符* 不是一回事。在指针变量说明中,“”是类型说明符,表示其后的变量是指针类型。而表达式中出现的“”则是一个运算符用以表示指针变量所指的变量。
感受代码:
int main()
{
int b=2,a=1,*pa,*pb;
pa=&a;
pb=&b;
//pa=pb;
cout<<a<<endl<<b<<endl;
cout<<*pa<<endl<<*pb;
return 0;
}
引发问题的操作:
int main(void)//危险的不合适的操作,指针变量p未赋予地址,就对其取值。
{
int i=10;
int *p;
*p=i;
cout<<*p;
}
指针的算术运算:
int main()//指针的算术运算
{
int b[5]= {12,23,34,45,65};
int *p=&b[0];
p++;
cout<<"*p="<<*p<<endl;
cout<<"p="<<p<<endl;
*p++;
cout<<"*p="<<*p<<endl;
cout<<"p="<<p<<endl;
(*p)++;
cout<<"*p="<<*p<<endl;
cout<<"p="<<p<<endl;
*++p;
cout<<"*p="<<*p<<endl;
cout<<"p="<<p<<endl;
++*p;
cout<<"*p="<<*p<<endl;
cout<<"p="<<p<<endl;
return 0;
}
传值和传址*的区别(重要):
如下三个swap只有第一个产生实际作用,因为它没有对形参(这里是指针变量)进行直接变动操作,而是利用指针变量改变了它指向的值。其余两个函数不能发挥作用,因为他们对形参进行了直接变化,这个变化不可以传递到main函数中。
int main()//指针变量做参数,交换a与b的值
{
// void swap(int *p1,int *p2);
void swap(int,int);
int *pointer1,*pointer2,a,b;
cin>>a>>b;
pointer1 = &a;
pointer2 = &b;
if(a<b)
swap(a,b);
cout<<"max="<<a<<" "<<"min="<<b<<endl;
return 0;
}
void swap(int *p1,int*p2)//可行的利用指针交换指向的内容,绕过了“单项传递”,达到交换值的目的
{
int temp;
temp = *p1;
*p1 = *p2;
*p2 = temp;
}
void swap(int x,int y)//暴力膜不可取,直接换是不行的,实参到形参为单项传递,形参值的变化无法传给实参
{
int temp;
temp = x;
x=y;
y= temp;
}
void swap(int *p1,int*p2)//也是不可取的,形参实参为指针变量,试图改变指针变量是无法对实参的指针变量产生影响。
{
int *temp;
temp = p1;
p1 = p2;
p2 = temp;
}
5、指针与数组:
int a[10], *pa; pa=a; pa =&a[0]; //等价
引用一个数组元素,有3种方法:
(1)下标法: a[i]
(2)数组名地址法: *(a+i)
(3)指针法: ①指针地址法: *(pa+i) ②指针下标法: pa[i]
int main()//练习用不同的方法表示二维数组的元素
{
int a[3][4]= {{5,6,7,8},{9,10,11,12},{13,14,15,16}};
int i,j , (*p)[4];
for(i=0; i<3; i++)
{
p=&a[i];
cout<<(*p)[0]<<(*p)[1]<<(*p)[2]<<(*p)[3]<<'\n';
}
p=a;
for(i=0; i<3; i++)
for(j=0; j<4; j++)
{
cout<<*(*(p+i)+j)<<*(p[i]+j)<<*(*(a+i)+j)<<'\n';
}
}
```c
![在这里插入图片描述](https://img-blog.csdnimg.cn/20181113210903351.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pTSlhNQg==,size_16,color_FFFFFF,t_70)**![加粗样式](https://img-blog.csdnimg.cn/20181113210908220.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0pTSlhNQg==,size_16,color_FFFFFF,t_70)
**指针数组:**
```,
int main()//指针数组举例
{
float a[ ] = {100,200,300,400,500};
float *p[5] = {&a[0],&a[1],&a[2],&a[3],&a[4]};
int i;
for(i = 0; i < 5; i++)
cout << *p[i] << '\t';
}
6、指针与函数:
int f1(int x,int y)//指向函数的指针举例,p指向函数的入口地址
{
int z;
z=(x>=y)?x:y;
return z;
}
int f2 (int a,int b)
{
return (a+b);
}
int main()
{
int (*p)(int,int);
int c,d;
p=f1;
c=p(5,10);
p=f2;
d=p(8,10);
cout<<c<<'\t'<<d;
}
五. 面向对象编程基本概念
1、面向过程的普遍特点对比面向对象的特点:
(面向过程)
①以功能为核心,以解决问题的过程和步骤为指导进行设计;
② 函数是构成程序的基本部分,也是软件复用的基本单位;
③ 函数之间相互独立,可以互相调用;
④ 程序结构清晰,模块化思想便于分工合作、提高开发效率;
⑤ 程序流程在编码时已经确定,用户不能更改;
⑥ 操作与数据相互分离,难以对数据和操作模块进行修改。
⑦ 设计者难以在开始就对需要解决的问题有比较全面的了解;
⑧ 难以进行程序功能扩展和维护
(面向对象)
① 强调程序中的对象以及对象之间的关系能够如实地反映问题
域中的事物及其关系。
② 整个源程序代码主要由若干个类构成;
③ 可以按需确定程序流程,通过对象之间相互发送消息建立各
对象之间的联系,完成整个程序的功能;
④ 具有抽象性、封装性、继承性和多态性的特征;
⑤ 类的大量加载会牺牲系统性能,降低运行速度。
2、面向对象基本概念
• 对象是具有一定的特征和行为的实体;
• 类是为了描述一组对象在结构和行为上的共性所创建的抽 象数据类型;是对具有相同或相似性质的对象的抽象描述;
• 类是创建对象的模板,从一个类实例化的每个对象具有相 同结构和行为;
• 一旦定义了一个类,程序员就可以从一个类创建这个类的 任意数目的对象,然后操作这些对象
3、封装
封装是将一组相关的概念聚集在一个单元内,并 且用单独的一个名字来引用。
面向对象的封装是将操作和表示状态的属性包装 在一个对象类型中,从而使状态只能通过封装体 提供的接口来访问和修改。
4、继承
继承允许在已有类的基础上定义新类(派生类),
派生类自动拥有已有类(基类)的属性和操作;
5、派生类可以增加自己特有的功能,也可以修改继
承得到的功能(覆盖)。
6、多态
多态性是一种机制,指一个操作名或属性名可在多个程序 单元中定义,且在各个程序单元中有不同的实现。
• 类的多态性是指在一般类中定义的属性或行为,被特殊类 继承之后,可以具有不同的数据类型或表现出不同的行为。
• 分为两个方面:
– 静态多态:通过函数重载、运算符重载或模板实现。
– 动态多态:通过类的继承关系和虚函数机制实现。
封装和信息隐藏
类把数据和对数据的操作封装在一个结构中;
• 数据一般被限定为私有的,在类的外部只能通过公有的接 口访问私有的数据。
• 类的声明和类的实现一般放到不同的两个文件中;
• 为了隐藏实现的细节,类的实现文件以目标文件的方式提 供;
• 为了隐藏数据成员,在实现文件中定义数据结构体,在类 的声明中通过指针访问数据结构体
7、优势:
– 加强了类的安全性与一致性;
– 类的改变可不影响使用该类的程序的正常运行。
– 对于使用者,不需要了解过多的细节就可以使用对象, 降低了操纵该类型的复杂程度;
– 对于设计者,有利于保护类的内部设计不被破坏或窃取
8、类的定义
class <类名>
{
public: <数据成员或成员函数的声明>
protected: <数据成员或成员函数的声明>
private: <数据成员或成员函数的声明>
};
代码示例:
#include <iostream>
using namespace std;
class Rectangle
{
private:
int left , top , right , bottom ;
public:
Rectangle(int l, int t, int r,int b)
{
left=l ;
top=t ;
right=r ;
bottom=b;
}
Rectangle()
{
left=0 ;
top=0 ;
right=0 ;
bottom=0 ;
}
void print()
{
cout<<left<<" "<<top<<" " ;
cout<<right<<" "<<bottom<<'\n';
}
};
int main()
{
Rectangle r1(100,200,300,400);
r1.print();
Rectangle r2;
r2.print();
return 0;
}