中国大学MOOC程序设计与算法(三)笔记:第一周 从C到C++

第一周 从C到C++
1.引用
2.const关键字
3.动态内存分配
4.内联函数,函数重载,函数缺省参数
5.类和对象的基本概念

1.引用
定义引用,并将其初始化为引用某个变量,就等价于这个变量,相当于它的别名
int n = 4;
int & r = n;//r引用n,对n或r的任何改变,都将改变对方
r的变量类型是 int &,不是int

注意:
1)定义引用时一定要将其初始化成引用某个变量,不能只定义不初始化;
2)初始化之后就一直引用这个变量,不能再引用其他变量了;
3)引用只能引用变量,不能引用常量和表达式。

引用类型变量可以作为函数形参

void swap(int & a, int & b)//a,b等价于n1,n2
{
	int tmp;
	tmp = a;a=b;b=a;
}
int n1,n2;
swap(n1,n2)

//C语言中没有&引用,形参是无法改变变量值的,只能传入指针

引用可以作为函数的返回值

int n=4;
int & setValue(){return n;}//返回引用的全局变量n
setValue()=40;//对全局变量n重新幅值了

常引用

int n;
const int & r = n;

特点:常引用不能修改其引用的内容
r = 200;//编译出错

常引用和非常引用的转换

const T & 和 T &是不同类型
T &类型引用或T类型变量可以初始化const T &类型的引用
但是,const T类型变量和const T &类型引用不能初始化T &类型的引用,除非进行强制类型转换

2.const关键字用法
const double PI = 3.141592653589793;

常量指针

不可以通过常量指针修改其指向的内容

int n,m;
const int *p = &n;
*p = 5;//编译出错
p = &m;//ok,可以修改指向

不可以把常量指针赋值给非常量指针,但是反过来可以

const int * p1;
int * p2;
p1 = p2;//ok
p2 = p1;//编译出错
p2 = (int *)p1;//ok,强制类型转换

将常量指针作为函数参数,可以避免函数内部不小心修改参数指针指向的地方

void myprint(const char * p)
{
	strcpy(p, "this");//编译出错,参数是常量指针,函数内不能修改
	printf("%s",p);//ok
}

3.动态内存分配
new运算符实现动态内存分配

第一种用法,分配一个变量

P = new T;
T是任意类型名,P是类型为T *的指针。动态分配出一片大小为sizeof(T)的内存空间,并将该内存空间的起始地址赋值给P。

int * pn;
pn = new int;
*pn = 5;

第二种用法,分配一个数组

P = new T[N];
分配出一片大小为N*sizeof(T)字节的内存空间,并将起始地址赋值给P。

int * pn;
int i = 5;
pn = new int[i*20];
pn[0]  = 20;//ok
pn[100] = 30;//编译没问题,但是数组越界

new运算符的返回值类型

new T;
new T[n];
返回值类型都是T *
int * pn = new int;

delete用来释放new动态分配出来的内存,否则该内存空间会被一直占用:delete 指针

int * p = new int;
*p = 5delete p;//该指针必须指向new出来的空间,否则会出错
delete p;//异常,不能被delete多次

delete释放动态分配的数组,在指针前要加一个[],表明是一个数组

delete [] 指针;//该指针必须指向new出来的一个数组

int * p = new int[10];
p[0] = 1;
delete [] p;

4.内联函数,函数重载,函数缺省参数

内联函数

1)函数调用是有时间开销的,如果函数本身只有几条语句,执行很快,且要被反复执行很多次,相比之下,调用函数产生的开销就要比函数执行大很多了;
2)为了减少函数调用的开销,C++引入内联函数机制。编译器处理对内联函数的调用语句时,直接将这个函数的代码插入到调用的地方,避免产生调用函数的语句,适用于反复调用同一个简单函数的情况,节省调用指令执行的时间。
内联函数的定义:只需要在函数定义前加“inline”关键字,函数本身写法不变,比如:

inline int max(int a,int b)
{
	if(a > b) return a;
	return b;
}

函数重载

一个或多个函数名字相同,但是参数个数或参数类型不相同,叫做函数重载
以下三个函数是重载关系:
int max(double f1, double f2){ }
int max(int n1, int n2){ }
int max(int n1, int n2, int n3){ }
函数重载让函数命名更简单
编译器根据调用语句中的实参的个数和类型判断调用的是哪个

max(3.4, 2.5);
max(2,4);
max(1,2,3);
max(3,2.4);//报错,二义性

重载关系的前提是函数名和返回类型都相同,如果函数名相同但返回类型不同,则不是重载,是重复定义。

函数的缺省参数

定义函数是可以让最右边的若干个连续若干个参数缺省,那么调用时,若相应位置不写参数,参数就是缺省值

void func(int x1, int x2 = 2, int x3 = 3{ }
func(10);//等价于func(10,2,3)
func(10,8);//等价于func(10,8,3)
func(10, , 8);//错误,只能是最右边连续若干个

意义:提高程序的可扩充性,即如果某个写好的函数需要添加新的参数,而原先那些调用这个函数的语句未必需要使用新增的参数,那么为了避免对圆心那些函数调用语句的修改,可以使用缺省参数。

5.类和对象的基本概念

结构化程序设计

C语言使用结构化程序设计:程序=数据结构+算法
结构化程序设计的不足:
1)结构化程序设计中,函数和其所操作的数据结构,没有直观的联系。
2) 随着程序规模的增加,程序逐渐难以理解,很难一下子看出来:
某个数据结构到底有哪些函数可以对它进行操作?
某个函数到底是用来操作哪些数据结构的?
任何两个函数之间存在怎样的调用关系?
3)结构化程序设计没有“封装”和“隐藏”的概念。要访问某个数据结构中的某个变量,就可以直接访问,那么当该变量的定义有改动的时候,就要把所有访问该变量的语句找出来修改,十分不利于程序的维护、扩充。
4) 难以查错,当某个数据结构的值不正确时,难以找出到底是那个函数导致的
5)重用:在编写某个程序时,发现其需要的某项功能,在现有的某个程序里已经有了相同或类似的实现,那么自然希望能够将那部分代码抽取出来,在新程序中使用
6)在结构化程序设计中,随着程序规模的增大,由于程序大量函数、变量之间的关系错综复杂,要抽取这部分代码,会变得十分困难。
7)总之,结构化的程序,在规模庞大时,会变得难以理解,难以扩充(增加新功能),难以查错,难以重用

面向对象的程序设计方法:能够较好解决上述问题

面向对象的程序 = 类 + 类 + …+ 类
设计程序的过程,就是设计类的过程。
面向对象的程序设计方法:
1) 将某类客观事物共同特点(属性)归纳出来,形成一个数据结构(可以用多个变量描述事物的属性);
2)将这类事物所能进行的行为也归纳出来,形成一个个函数,这些函数可以用来操作数据结构(这一步叫“抽象”)。
3) 然后,通过某种语法形式,将数据结构和操作该数据结构的函数“捆绑”在一起,形成一个“类”,从而使数据结构和操作该数据结构的算法呈现出显而易见的紧密关系,这就是“封装”。
4) 面向对象的程序设计具有“抽象”,“封装”,“继承”,“多态”四个基本特点

举例:写一个程序,输入矩形的长和宽,输出面积和周长

1)将长、宽变量和设置长,宽,求面积,以及求周长的三个函数“封装”在一起,就能形成一个“矩形类”。
2) 长、宽变量成为该“矩形类”的“成员变量”,三个函数成为该类的“成员函数” 。 成员变量和成员函数统称为类的成员。
3)实际上,“类”看上去就像“带函数的结构”

class CRectangle
{
	public:
		int w, h;
		int Area() {
			return w * h;
		}
		int Perimeter(){
			return 2 * ( w + h);
		}
		void Init( int w_,int h_ ) {
			w = w_; h = h_;
		}
}; // 必须有分号

int main( )
{
int w,h;
CRectangle r; //r 是一个对象
cin >> w >> h;
r.Init( w,h);
cout << r.Area() << endl <<r.Perimeter();
return 0;
}

通过类,可以定义变量。类定义出来的变量,也称为类的实例,就是我们所说的“对象” 。
C++中,类的名字就是用户自定义的类型的名字。可以象使用基本类型那样来使用它。CRectangle 就是一种用户自定义的类型。

和结构变量一样,对象所占用的内存空间的大小,等于所有成员变量的大小之和。

对于上面的CRectangle类,sizeof(CRectangle) =8。成员函数也需要占用空间,但是一个类的所有对象的成员函数共享空间,也就是只存一份,不算是对象的空间。

每个对象各有自己的存储空间。一个对象的某个成员变量被改变了,不会影响到另一个对象。

对象间的运算

和结构变量一样,对象之间可以用 “=”进行赋值,但是不能用 “==”,“!=”,“>”,“<”“>=”“<=”进行比较,除非这些运算符经过了“重载”。

使用类的成员变量和成员函数

用法1:对象名.成员名

CRectangle r1,r2;
r1.w = 5;
r2.Init(5,4);

用法2. 指针->成员名

CRectangle r1,r2;
CRectangle * p1 = & r1;
CRectangle * p2 = & r2;
p1->w = 5;
p2->Init(5,4);  //Init作用在p2指向的对象上

用法3:引用名.成员名

CRectangle r2;
CRectangle & rr = r2;
rr.w = 5;
rr.Init(5,4); //rr 的值变了,r2 的值也变

类的成员函数和类的定义可以分开写:

class CRectangle
{
	public:
		int w, h;
		int Area() ;//成员函数仅在此处声明
		int Perimeter();
		void Init( int w_,int h_ );
};
int Crectangle::Area() {
	return w * h;
}
int Crectangle::Perimeter(){
	return 2 * ( w + h);
}
void Crectangle::Init( int w_,int h_ ) {
	w = w_; h = h_;
}

"Crectangle::"说明后面的函数是Crectangle类的成员函数而非普通函数。那么,一定要通过对象或对象的指针或对象的引用才能调用,不可以作为普通的全局函数被调用。

发布了20 篇原创文章 · 获赞 1 · 访问量 152

猜你喜欢

转载自blog.csdn.net/weixin_42503072/article/details/104684857