Hello,C++(2)
1、C++对C的扩展
1)C++需要命名空间
namespace,是指标识符的各种可见范围
using namespace std;这样命名空间std内定义的所有标识符都有效(曝光),就好像它们被声明为全局变量一样。
2)C++命名空间的使用
使用整个命名空间:using namespace name;eg:using namespace std
使用命名空间中的变量:using name::variable;eg:,std::cout
3)变量定义的位置不同
C语言中的变量都必须在作用域开始的位置定义
C++中更强调语言的“实用性”,所有的变量都可以在需要使用时再定义
4)C++中所有的变量和函数都必须有类型
C语言中的默认类型在C++中是不合法的
5)C++在C语言的基本类型系统之上增加了bool
C++中的bool可取的值只有true和false
理论上bool只占用一个字节,C++编译器会在赋值时将非0值转换为true,0值转换为false
2、const分类总结
1)修饰常量
用const修饰的变量是不可变的,以下两种定义形式在本质上是一样的:
const int a = 10;
int const a = 10;
2)修饰指针
如果const位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
//指针所指向的内存空间,不能被修改
int operatorTeacher01(const Teacher *pT)
{
//pT->age = 10;
return 0;
}
如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。
顶层const:指针本身是常量;*const p;
底层const:指针所指的对象是常量。const *p;
引用的const都是底层const:const int &a = b;
//指针变量本身不能被修改
int operatorTeacher02( Teacher * const pT)
{
pT->age = 10;
//pT = NULL; //指针变量不可更改
return 0;
}
因此,推荐使用int const* p,而不是使用const int* p(虽然两者意义完全一样),这样更容易理解。
int a = 10;
const int* p = &a; // 指针指向的内容不能变
int const* p = &a; // 同上
int* const p = &a; // 指针本身不能变
const int* const p = &a; // 两者都不能变
int const* const p = &a; // 同上
3)修饰引用
const int& b = a;
int const& b = a;//作用同上
4)修饰函数参数
用const修饰函数参数,传递过来的参数在函数内不可以改变。
void func (const int& n)
{
n = 10; // 编译错误
}
5) 修饰函数返回值
用const修饰函数返回值的含义和用const修饰普通变量以及指针的含义基本相同。
const int* func() // 返回的指针所指向的内容不能修改
{
// return p;
}
6)修饰类成员变量
用const修饰的类成员变量,只能在类的构造函数初始化列表中赋值,不能在类构造函数体内赋值。
class A
{
public:
A(int x) : a(x) // 正确
{
//a = x; // 错误
}
private:
const int a;
};
7)修饰类成员函数
用const修饰的类成员函数,在该函数体内不能改变该类对象的任何成员变量, 也不能调用类中任何非const成员函数。
class A
{
public:
int& getValue() const
{
// a = 10; // 错误
return a;
}
private:
int a; // 非const成员变量
};
8)修饰类对象
用const修饰的类对象,该对象内的任何成员变量都不能被修改。
因此不能调用该对象的任何非const成员函数,因为对非const成员函数的调用会有修改成员变量的企图。
class A
{
public:
void funcA() {}
void funcB() const {}
};
int main
{
const A a;
a.funcB(); // 可以
a.funcA(); // 错误
const A* b = new A();
b->funcB(); // 可以
b->funcA(); // 错误
}
9)在类内重载成员函数
class A
{
public:
void func() {}
void func() const {} // 重载
};
10)const与宏定义的区别
C++中的const常量类似于宏定义
const int c = 5; ≈ #define c 5
C++中的const常量与宏定义不同
const常量是由编译器处理的,提供类型检查和作用域检查
宏定义由预处理器处理,单纯的文本替换
#define PI 3.1415926535
const double PI = 3.1415926535;
3、引用总结
1)作用:引用可以看作一个已定义变量的别名
引用仅是变量的别名,而不是实实在在地定义了一个变量,因此引用本身并不占用内存,而是和目标变量共同指向目标变量的内存地址.
表达式中的取地址符&不再是取变量的地址,而是用来表示该变量是引用类型的变量。
2)用法:
a.普通引用在声明时必须用其它的变量进行初始化,引用作为函数参数声明时不进行初始化
b.引用作为其它变量的别名而存在,因此在一些场合可以代替指针,并且有更好的可读性和实用性(左右作用相同)
指针的间接赋值(右)
1定义两个变量 (一个实参一个形参)
2建立关联 实参取地址传给形参
3*p形参去间接的修改实参的值
引用在实现上,只不过是把:间接赋值成立的三个条件的后两步和二为一,直接修改引用变量的名字就行。
3)应用:
1、引用作为参数
引用的一个重要作用就是作为函数的参数。以前的C语言中函数参数传递是值传递,如果有大块数据作为参数传递的时候,采用的方案往往是指针,因为这样可以避免将整块数据全部压栈,可以提高程序的效率。但是现在(C++中)又增加了一种同样有效率的选择(在某些特殊情况下又是必须的选择),就是引用。
(1)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(2)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用\"*指针变量名\"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
2、常引用
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。
常引用声明方式:const 类型标识符 &引用名 = 目标变量名;
用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性。
3、引用作为返回值
要以引用返回函数值,则函数定义时要按以下格式:
类型标识符 &函数名 (形参列表及类型说明)
{ 函数体 }
说明:
(1)以引用返回函数值,定义函数时需要在函数名前加&
(2)用引用返回一个函数值的最大好处是,在内存中不产生被返回值的副本。
以下程序中定义了一个普通的函数fn1(它用返回值的方法返回函数值),另外一个函数fn2,它以引用的方法返回函数值。
#include <iostream>
using namespace std;
float temp;//定义全局变量temp
float fn1(float r);//声明函数fn1
float &fn2(float r);//声明函数fn2 r
float fn1(float r){//定义函数fn1,它以返回值的方法返回函数值
temp=(float)(r*r*3.14);
return temp;
}
float &fn2(float r){//定义函数fn2,它以引用方式返回函数值
temp=(float)(r*r*3.14);
return temp;
}
int main(){
float e=10.0;
float a=fn1(10.0);//第1种情况,系统生成要返回值的副本(即临时变量)
// float &b=fn1(10.0); //第2种情况,可能会出错(不同 C++系统有不同规定)
/*error: invalid initialization of non-const reference of type 'float&' from an rvalue of type 'float'
*/
//不能从被调函数中返回一个临时变量或局部变量的引用
float c=fn2(10.0);//第3种情况,系统不生成返回值的副本
//可以从被调函数中返回一个全局变量的引用
float &d=fn2(10.0); //第4种情况,系统不生成返回值的副本
e=d;
cout<<"a="<<a<<",c="<<c<<",d="<<d<<",e="<<e<<endl;
//a=314,c=314,d=314
return 0;
}
引用总结
(1)在引用的使用中,单纯给某个变量取个别名是毫无意义的,引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不如意的问题。
(2)用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率,且通过const的使用,保证了引用传递的安全性。
(3)引用与指针的区别是,指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
(4)引用本身并非对象, 引用没有地址,一旦定义了引用,就无法绑定到其他对象。而指针可以指向新的对象地址。
4、C++对C的函数扩展
1) inline内联函数
-
- 作用:
- 相当于宏替换代码片段,C++编译器直接将函数体插入在函数调用的地方
- 要点:
- 内联函数声明时inline关键字必须和函数定义结合在一起,否则编译器会直接忽略内联请求
- 不能存在任何形式的循环语句、不能存在过多的条件判断语句
- 写在类里面的成员函数会自动定义成内联函数,所以类里面的函数大都写得比较简单,事实上大部分的类的成员函数声明和定义是分开的,声明在里面,定义在外面
- 内联函数相对于普通函数的优势只是省去了函数调用时压栈,跳转和返回的开销。因此,当函数体的执行开销远大于压栈,跳转和返回所用的开销时,那么内联将无意义。
- 现代C++编译器能够进行编译优化,因此一些函数即使没有inline声明,也可能被编译器内联编译。
- 举例:
#include "iostream" using namespace std; #define MYFUNC(a, b) ((a) < (b) ? (a) : (b)) inline int myfunc(int a, int b) { return a < b ? a : b; } int main() { int a = 1; int b = 3; //int c = myfunc(++a, b); int c = MYFUNC(++a, b); printf("a = %d\n", a); printf("b = %d\n", b); printf("c = %d\n", c); printf("Press enter to continue ..."); system("pause"); return 0; }
- 结论
- 1)内联函数在编译时直接将函数体插入函数调用的地方
- 2)inline只是一种请求,编译器不一定允许这种请求
- 3)内联函数省去了普通函数调用时压栈,跳转和返回的开销
- 作用:
2)默认参数
作用:C++中可以在函数声明时为参数提供一个默认值,当函数调用时没有指定这个参数的值,编译器会自动用默认值代替
使用规则:
只有参数列表后面部分的参数才可以提供默认参数值
一旦在一个函数调用中开始使用默认参数值,那么这个参数后的所有参数都必须使用默认参数值
举例:
void printABC(int a, int b, int x = 3, inty=4, int z = 5)
{
printf("x:%d\n",x);
}
3) 函数占位参数
-
- 作用:函数占位参数主要用来和默认参数结合,以便兼容C语言中的不规范写法
- 使用规则:
- 占位参数只有参数类型声明,而没有参数名声明
- 一般情况下,在函数体内部无法使用占位参数
- 举例:
int func(int a, int b, int ) { return a + b; } int main(int argc, char *argv[]) { printf("func(1, 2, 3) = %d\n", func(1, 2, 3)); printf("Press enter to continue ..."); getchar(); return 0; }
4)函数重载
-
- 作用:
- 用同一个函数名定义不同的函数,当函数名和不同的参数搭配时函数的含义不同
- 判断重载的标准:
- 参数个数或类型不同
- 注意:函数返回值不是函数重载的判断标准,返回值不同其他相同属于重复定义
- 举例:
#include <iostream> using namespace std; void Max(int a, int b) { cout << "Max 1" << endl; } void Max(double a, double b) { cout << "Max 2" << endl; } void Max(double a, double b, double c) { cout << "Max 3" << endl; } int main() { Max(3, 4); //调用 int Max(int, int) Max(2.4, 6.0); //调用 double Max(doubleA double) Max(1.2, 3.4, 5); //调用 double Max(double, double, double) Max(1, 2, 3); //调用 double Max(double, double, double) Max(3, 1.5); //编译出错:二义性 return 0; }
- 作用: