C++对C的扩充(体现在面向过程程序设计部分)

1、C++的输入输出

C++的输入输出在保留C语言的输入输出系统之外,还利用继承的机制创建出一套自己的方便、一致、安全、可扩充的输入输出系统,即C++输入输出(I/O)流类库。

有了“流”的思想,对所有的输入输出就都是一样的了,输入时,字节流从输入设备流向内存,输入设备可以是键盘、磁盘、光盘等,使用时创建输入设备的输入流对象,然后通过>>运算符将数据从输入流对象读入内存的变量或其他数据结构中,输出时,字节流从内存流向输出设备,包括显示器、磁盘或其他输出设备,使用时创建输出设备的输出流对象,然后通过<<运算符讲数据从内存输出到输出流对象,完成输出操作。

2、C++对C语言数据类型的扩展

基本类型增加了布尔型,构造类型增加了类类型,此外还增加了引用类型。

细节上的应用区别

1)常变量 

为了使常量 也能像变量那样进行类型检查,C++提供了用const定义常变量的方法,如:const float PI =3.14159; float const PI = 3.14159;

PI 前面有一个关键字const,就表示PI是一个常变量,定义PI时必须对PI进行初始化,之后PI的值就不可能再改变了,任何修改PI或者给PI赋值的语句都是非法的,程序不能通过编译。也可以使用表达式对常变量进行初始化,系统会先计算表达式的值,然后再将值赋给常变量。另,系统在静态存储区给常变量分配内存单元,而普通变量是在动态存储区分配内存单元的。

2) 指针

空指针 int *p = NULL;(或 0)

野指针 int *p;(未初始化的指针)

一般 都在定义指针时将指针初始化为空指针。程序中要彻底杜绝使用野指针。一旦指针被free,不再使用时,及时置为NULL。


指针与const   

const 与 * 之间的相对位置关系有两个,可以形成三种指针 

 只在*之前有const的指针,称为指向const变量的指针

1)如果一个变量已经被声明为const常量,则只能用指向const变量的指针去指向它, 而不能用一般的指针(指向非const型变量的指针)去指向他。

2)指向常变量的指针也可以指向普通变量,此时,可以通过指针访问该变量,但是不能通过指针改变该变量的值,但是可以通过变量本身修改值。

3)指向const变量的指针本身可以修改,即可以指向其他变量。

4)两种形式:const int *p = NULL ; int const *p = NULL;

/** 指向常变量的指针  **/

#include<iostream>

using namespace std;
int main()
{
	const int *p = NULL;
	int const *pp = NULL;//也可以这么定义 
	const int a = 0;
	int const c = 10;
	int b = 15;
	p = &a;
	cout<<"*p="<<*p<<endl;
	p = &b;               //可以更换p指的单元  
	cout<<"*p="<<*p<<endl;
//	*p = 200;             //错误,不能通过指针p修改p所指向单元内容 
	b=200;                //可以通过变量本身修改值 
	cout<<"*p="<<*p<<endl; //同步更新修改后的值 
	 
	return 0;
} 

指向const变量的指针最常用于函数的形参,目的是保护形参指针所指定向的实参变量,使他在函数的执行过程中不被修改,在函数调用时,其对应的实参既可以是指向const变量的指针,也可以是指向非const变量的指针。例如,字符串处理的函数一般都是这样处理的。


char *strncat(char *s1, const char *s2, size_t n); //s1是需要修改的,s2不需要修改,最好不要修改,用const 保护

int strcmp(const char *s1, const char *s2); //函数功能仅仅是比较,两个字符串都不需要修改,用const保护


另,,采用这种方法,还可兼容当上述字符串参数为字符串常量时的情况。


如 strcmp("hello","world");

"hello","world"会产生一个临时变量,在C++中,这些临时变量都是const类型的。


只在*之后又const的指针,称为const指针

指针变量的值不能改变,定义时就必须初始化,但可以通过指针修改所指向单元的内容,不能更换指向的单元


*前后都有const的指针,称为指向const变量的const指针

既不可以通过指针修改所指向单元的内容,又不能修改指针指向的单元



void指针 无类型指针,可以指向任意类型的数据,但是在使用前必须进行强制类型转换

void *memcpy(void dest,const void*src,size_t len);


3、内存管理 new和delete运算符

C语言中使用  malloc 和free函数  需要包含头文件 stdlib.h 或 alloc.h

void *malloc(int size)  和 void free(void * block)

C++ 新增new 和 delete

int *p=NULL;

p = new int;也可以初始化,p = new int(100);

delete p;

p=NULL;

数组

int *p = new int[100];

int *p = new int[n];n是变量

delete[] p;

上述使用必须配对出现


4、引用

引用就是某一变量的别名,对引用的操作与对该变量直接操作完全一样,引用的声明方式: 类型标识符 & 引用名 = 目标变量名;

举例 :  

int x = 100;
int &rx = x;
#include<iostream>

using namespace std;

int main()
{
	int x=100;
	int &rx=x;
	cout<<"rx="<<rx<<endl;
	rx = 200;
	cout<<"x="<<x<<endl; 
	return 0;
} 
程序运行结果:

rx=100
x=200

声明引用时,引用前面的类型标识符是指目标变量的类型,且必须同时对其进行初始化,即声明它代表哪一个变量。引用声明完毕后,相当于目标变量有两个名称,即该目标变量原名称和引用名,且不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,因此引用本身不占存储单元,系统也不给引用分配存储单元。


引用的使用,引用传参

#include<iostream>

using namespace std;

void swap(int &rx,int &ry)
{
	int temp = rx;
	rx = ry;
	ry = temp;
}

int main()
{
	int x=100,y=200;
	cout<<"before swap:";
	cout<<"x="<<x<<","<<"y="<<y<<endl;
	swap(x,y);
	cout<<"after swap:";
	cout<<"x="<<x<<","<<"y="<<y<<endl;
	return 0;
} 
运行结果:

before swap:x=100,y=200
after swap:x=200,y=100

上述程序中,将形参的数据类型从int型变为int型引用,这种传递参数的方式叫作引用传参。形参rx,ry是两个int型引用,swap函数调用时,swap的rx,ry被初始化为main函数变量x,y的别名,访问swap的rx,ry和main函数的x,y效果完全一样。

这种传递方式书写简单,易于理解,而且可以提高程序的执行效率(不需要复制数据),在许多情况下可以代替指针的操作。

c++提供引用机制,主要是利用它作为函数参数,以扩充函数传递数据的功能。

进一步说明:

1、不能建立void类型的引用,任何实际存在的变量都是属于非void型的。

2、不能建立数组的引用。引用 只能是 变量对象 的引用。数组是具有某种类型的数据的集合,其名字表示该数组的起始地址而不是一个变量。所以不能建立数组的引用。

3、变量的引用也有地址,与被引用的变量共享,将其赋给一个指针,指针指向的是原来的变量。

int a = 3;
int &b = a;
int *p = &b;
p 相当于指向 a;

4、可以建立指针变量的引用,指针变量也是变量。

int a = 3;
int *p = &a;
int * &rp = p;// rp是一个指向 int型变量 的 指针变量p 的 引用
引用不是独立的数据类型, 语句 int & *p=&a; 是错误的。

5、 常引用 

const 类型标识符 & 引用名 = 目标变量名;

用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到引用的安全性

int a = 3;
const int &ra = a;
ra = 1; //错误,不能通过引用名修改
a = 1; //正确

常引用 用作函数的形参

void Show(const string & s)
{
	s = "hello word!";//错误,不能正常修改引用形参的值 
	cout<<s<<endl; 
}
利用常引用作为函数形参,既能提高程序的执行效率,又能 保护传递给函数的数据不在函数中被改变,达到保护实参的目的。

还有兼容性问题的举例

举例:
string StrFunc();

void Show(string &s);

则下面的表达式将是非法的:

Show(StrFunc());

Show("hello world");

原因在于StrFunc()和"hello world"串都会产生一个临时变量,而在c++中,这些临时变量都是const类型的。因此上面的表达式都是试图将一个const类型的变量转换为非const类型,这是非法的。

应该 改成 void Show( const string &s);

因此,引用型形参应该在能被定义成const的情况下,尽量定义成const(在只能使用,不能修改的情况的下,例如swap函数就不符合这类情况),这样,行数调用时的实参既可以是const型,也可以使非const型,但是这样,对于形参来说,就只能使用,不能修改了。

6、可以用常量或表达式(临时变量,也即常量)对引用进行初始化,但此时必须用const进行声明。

例如

int a=3;
const int &b = a+3;
边柜系统将 const int &b = a+3; 转换为:

int temp = a+3;
const int &b = temp;

但是临时变量是内部实现的,用户无法访问临时变量。


还有一种情况,用不同类型的变量对之初始化。

double d = 3.14159;
const int &a = d;
编译系统将“const int &a = d;”转换为:

int temp = d;
const int &a = temp;
因此, 引用a其实是临时变量temp的引用,而非d的引用;在该例子中,必须用const


7、引用作为函数的返回值

函数的返回值为引用表示该函数的返回值是一个内存变量的别名,可以将函数的调用作为一个变量来使用,可以为其赋值。但是不能返回函数内部局部变量的引用,函数返回时,内部已经销毁了。

#include<iostream>

using namespace std;

int &Max(int &x,int &y)
{
	return (x>y)? x:y;
}

int main()
{
	int a=2,b=3;
	cout<<"a="<<a<<",b="<<b<<endl;
	Max(a,b)=4;
	//由于函数的返回值为引用,是值较大的那个变量的引用,所以可以为函数赋值
	//为函数赋的值实际赋给了两个参数中的较大者,所以a的值为2,b的值为4;
	cout<<"a="<<a<<",b="<<b<<endl; 
	return 0;
} 
运行结果如下:

a=2,b=3
a=2,b=4

定义返回引用的函数时,注意不要返回对该函数内的自动变量的引用。否则,因为自动变量的生存期仅局限于函数的内部,当函数返回时,自动变量就消失了,函数就会返回一个无效的引用。函数返回的引用是对某一个函数参数的引用,而且这个参数本身也是引用类型,这样才能保证函数返回的引用有意义。

指针与引用的区别:

(1) 从内存分配上看:指针变量需要分配内存区域,而引用不需要分配内存区域。

(2)指针可以多级,而引用只能是一级。

<span style="white-space:pre">	</span>int aa = 2;
	int &ra=aa;
	int &rb=ra;
	rb=100;
	cout<<aa<<endl;
	cout<<ra<<endl;
	cout<<rb<<endl;
上述,引用ra、rb都是aa的引用,最终运行结果都是100,没有多级的概念。

(3)指针的值可以为NULL,但是引用的值不能为NULL,,并且引用在定义的时候必须初始化。
(4)指针的值在初始化后可以改变,即指向其他的存储单元,而引用在进行初始化后就不能再改变了

(5)指针和引用的自增(++)运算意义不一样。指针自增是指指针指向下一个内存单元,引用自增是指被引用的变量的值增1;


引用以简略的方式取代了某些条件下指针的作用,尤其适合函数的参数传递。但有些时候,引用还是不能替代指针,这样的情况如下:

(1)如果一个指针所指向的对象,需要分支结构加以确定,或者在途中需要改变它所指向的对象,那么在它初始化之后需要为它赋值,而引用只能再初始化时指定被引用的对象,所以不能胜任。

(2)有时一个指针的值可能是(void 指针),例如当把指针作为函数的参数类型或返回类型时,有时会用void指针表达特定的含义,引用没有类似的用法。

(3)函数指针无法 被引用替代

(4)用new动态创建的对象或数组,需要用指针来存储他的地址

(5)以数组形式传递大批量数据时(数组),需要用指针类型参数


5. 函数

面向过程的C++程序设计具有C语言的函数风格,而在面向对象的C++程序设计中,main函数之外绝大部分的函数被封装到了类中,调用函数一般通过类的对象来调用类里的函数的。

几点特殊的地方

(1)函数默认参数

在定义或声明函数时,给形参一个默认值,如果在调用时没有给该形参传递实参值,则使用默认值作为该形参的值;如果调用时给该形参传递了实参值,则使用实参的值作为该形参的值。

可以使用以下两种声明方式,其中默认参数必须放在右端,最好只在函数原型声明时指定默认值

int Max(int a,int b,int c=0);

int Max(int ,int ,int =0);

有时候会与函数重载出现二义性的问题。

(2)函数与引用的联合应用

在子函数中通过引用传递访问main函数的数组,这样可以方便快捷地传递大量的数据

#include<iostream>

using namespace std;

typedef int arr[8];

int main()
{
	void Func(arr &);
	int a[8]={1,2,3,4,5,6,7,8};
	for(int i=0;i<8;i++)
		cout<<a[i]<<" ";
	cout<<endl;
	Func(a);
	for(int i=0;i<8;i++)
		cout<<a[i]<<" ";
	return 0;
} 
void Func(arr &rx)
{
	rx[5]=2;
	rx[0]=10;
}
运行结果:

1 2 3 4 5 6 7 8
10 2 3 4 5 2 7 8

(3)函数与const

  const 修饰函数的参数      void Func(const int *a,const int & b);

const修饰函数的返回值   const T Func(); 或者 const T *Func(); 

const修饰整个函数        void Func() const; 不能修改数据成员,不能呢调用其他非const成员函数,const对象只能访问const函数

 (4)函数重载

同名函数,函数功能类似,只是所处理的数据类型不同。函数调用时编译系统会根据实参的数据类型和个数自动选择合适的Add函数的版本,根据参数表来匹配匹配度最高的。

函数重载需要函数参数的类型或个数必须至少有其中之一不同,函数返回值类型可以相同,也可以不同。单是,不允许参数的个数和类型都相同,而只有返回值类型不同。单是程序的可读性大大下降。

有重载时,为了避免出现二义性,必须完全匹配才行。

#include<iostream>

using namespace std;

int Add(int a,int b);
int Add(float a,float b); 

int main()
{
	float a=1.0;
	int  b =1.1;
	cout<<Add(a,b)<<endl;//会出现编译错误:[Error] call of overloaded 'Add(float&, int&)' is ambiguous
	return 0;
} 

int Add(int a,int b)
{
	cout<<"add in int"<<endl;
	return a+b;
}
int Add(float a,float b)
{
	cout<<"add in float"<<endl;
	return a+b;
}
当没有函数重载时,参数传递时会发生强制类型转换。

(5)内联函数

系统在编译时将所调用的函数代码直接嵌入到主调函数中,避免了函数调用的开销,适合规模较小(1~5)行而又频繁调用的情况,是一种空间换时间的策略。

#include<iostream>

using namespace std;

inline int Add(int a,int b);

int main()
{
	float a=1.0;
	int  b =1.1;
	
	cout<<Add(a,b)<<endl;
	return 0;
} 

inline int Add(int a,int b)
{
	cout<<"add in int"<<endl;
	return a+b;
}
C++ 规定不能作为内联函数的:递归,函数体内包含循环,含有switch,goto语句之类的复杂结构的函数,包含静态数据,数组的函数,具有较多代码的函数。其实具体的看编译器的支持程度。有的支持简单递归等情况。

(6)字符串变量、复数变量

string 类对象 (常用)、复数类模板(少用,即学即用)

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值