C++对C语言的扩展
2016-04-28 09:32
639 查看
一、C++对C的扩展:
(1)C面向过程加工的是一个个函数,C++面向对象加工的是一个个类;
(2)为什么要写成员函数?
class MyCircle { public : void setR(double d) { m_r = d; } /*double getS() //成员函数 { s = 3.14*m_r*m_r; return s; }*/ public: double m_r; double s = 3.14*m_r*m_r; //直接赋值 //初始化时,m_r是个随机值,所以s也是一个随机值。当在类外直接 //调用mycircle.s时是直接去s的内存空间拿值,并没有也不会执行 //3.14*m_r*m_r,即使给m_r赋值,mycircle.s也仍然是个随机值。 };
(3)namespace(名字空间、作用域);
所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。为了避免在大规模程序的设计中以及在程序员使用各种各样的C++库时,这些标识符的命名发生冲突,标准C++引入了关键字namespace(名字空间),可以更好地控制标识符的作用域。C中的命名空间:
在C语言中只有一个全局作用域;C语言中所有的全局标识符共享同一个作用域;标识符之间可能发生冲突。
C++中提出了命名空间:
命名空间将全局作用域分成不同的部分;不同命名空间中的标识符可以同名而不会发生冲突;命名空间可以相互嵌套。
namespace A { int a = 10; namespace B { int a = 20; } }
{ cout << A::a << endl; //10 //cout << B::a << endl; //编译错误 cout << A::B::a << endl; //20 //using namespace B; //编译错误 //cout << a << endl; using namespace A::B; cout << a << endl; //20 using namespace A; //cout << a << endl; //编译错误 cout << A::a << endl; //10, using 关键字把名字空间里面的标识符全部暴漏 cout << B::a << endl; //20 using namespace B; //这时,上面的using namespace A已经将B“暴漏”,所以可以找到。 cout << B::a << endl; //20 }
(4)实用性
C语言的变量都必须在作用域开始的位置定义(VC6.0——》X.c)!C++中更强调语言的“实用性”,所有的变量可以在需要使用时再定义。(5)struct类型功能加强
C语言的struct定义了一组变量的集合,C编译器并不认为这是一种新的类型;C++中的struct是一个新类型的定义声明。struct Student //在C++中,struct和class完成的功能是一样的 { char name[32]; //默认public,写不写无所谓 int age; }; void main() { //struct Student stu; //C语言Student stu;编译错误 Student stu; //C++ stu.age = 10; }
(6)C++中所有变量和函数都必须有类型
在C语言中:int f( );表示返回值为int,接受任意参数的函数,
比如:f(1,2,3,4);
int f(void);表示返回值为int的无参函数,
比如:f();
在C++中:
int f( );和int f(void)具有相同的意义,都表示返回值为int的无参函数。C++更加强调类型,任意的程序元素都必须显示指明类型。
(7)C++新增bool类型关键字
它只有0和1,没有其他值。非0都是1;(8)三目运算符
C语言中的三目运算符返回的是变量值,不能作为左值使用;(a < b ? a : b )= 30; //编译错误
C++本质::*(a < b ?&a
: &b )= 30;
C++中的三目运算符可直接返回变量本身,能作为左值使用,可以出现在程序的任何地方。(a > b ? a : b) = 30;直接编译通过。注意:可能三目运算符返回的值中如果有一个是常量值,则不能作为左值使用:(a < b ?1 :
b )= 30;
当左值的条件:要有内存空间;C++编译器帮助程序员取了一个地址而已。
(9)const表现
[b] http://blog.csdn.net/songshimvp1/article/details/50975332 [/b]
(10)引用
引用——别名1、引用必须初始化,引用作为函数参数声明时不进行初始化。
2、引用做函数参数的好处:
值传递,(1)有复制操作,如果是复杂数据类型,代价比较大;(2)因为复制,也就是说形参是副本,形参和实参是两个不同的变量,对形参进行修改后,对实参不起作用。指针,引用是变量的别名,在一些场合可以代替指针,引用相对于指针来说有更好的可读性和实用性。
3、引用本质:
引用有自己的内存空间,而且和指针占的内存空间大小一样。而且引用是一个常量!——Type& name——》Type* const name4、当函数返回值为引用时,若返回栈变量,不能成为其它引用的初始值,不能作为左值使用。
若返回静态变量或全局变量,可以成为其他引用的初始值,即可作为右值使用,也可作为左值使用,C++链式编程中,经常用到引用,运算符重载等。int getA1() { int a; a = 10; return a; } //返回a的一个副本 int& getA2() { int a; //如果是局部变量——栈上的引用 a = 10; return a; } int* getA3() { int a; a = 10; return &a; } //变量是 静态变量 或者 全局变量 int j1() { static int a = 10; a++; return a; } int& j2() { static int a = 10; a++; return a; } //函数当左值 int g1() { static int b = 10; b++; return b; } int& g2() { static int b = 10; b++; cout << b << endl; return b; } int main() { int a1 = getA1(); int a2 = getA2(); int &a3 = getA2(); //debug版本下,这个值不稳定。(函数中栈上的变量的内存空间被搞掉了)(多测试几次) int a4 = j1(); int a5 = j2(); int &a6 = j2(); cout << "a1:" << a1 << ",a2:" << a2 << ",a3:" << a3 << endl; cout << "a4:" << a4 << ",a5:" << a5 << ",a6:" << a6 << endl; //g1() = 100; //编译错误 g2() = 100; //打印 11 cout << g2() << endl; //打印101 101 return 0; }
5、指针引用
int m_value = 1; void func(int *&p) //p: 是指针的引用,main()方法里的 *pn,*p:是main()方法里的pn指向的内容。 { p = &m_value; // 也可以根据你的需求分配内存 p = new int; *p = 5; } int main(int argc, char *argv[]) { int n = 2; int *pn = &n; cout << *pn << endl; func(pn); cout << *pn <<endl; return 0; }
6、常引用
const Type& name = var;const引用让变量拥有只读属性 ,不能再用name去修改var的值了。Const int &a = 10;这种情况会单独为常量值分配内存,并将引用名作为这段空间的别名
普通引用相当于 int * const e;
const int &e 相当于
const int* const e
(11)inline内联函数
C++中内联编译的限制:不能存在任何形式的循环语句;不能存在过多的条件判断语句;函数体不能过于庞大;不能对函数进行取址操作;函数内联声明必须在调用语句之前;编译器对于内联函数的限制并不是绝对的,内联函数相对于普通函数的优势只是省去了函数调用时压栈,跳转和返回的开销。因此,当函数体的执行开销远大于压栈,跳转和返回所用的开销时,那么内联将无意义。
内联函数在最终生成的代码中是没有定义的,C++编译器直接将函数体插入在函数调用的地方,内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)。inline只是一种请求,C++编译器不一定准许函数的内联请求!
内联函数由编译器处理,直接将编译后的函数体插入调用的地方;宏代码片段 由预处理器处理, 进行简单的文本替换,没有任何编译过程。
一些函数即使没有inline声明,也可能被编译器内联编译。
二、函数重载:
判断标准:相同的函数名搭配不同的参数(参数的个数不同、参数的类型不同、参数的顺序不同),函数返回值不是判断函数重载的标准。编译器调用重载函数的准则:将所有同名函数作为候选者;尝试寻找可行的候选函数;精确匹配实参;通过默认参数能够匹配实参;通过默认类型转换匹配实参;
匹配失败:最终寻找到的可行候选函数不唯一,则出现二义性,编译失败。无法匹配所有候选者,函数未定义,编译失败。
函数重载的注意事项:重载函数在本质上是相互独立的不同函数(静态链编);函数返回值不能作为函数重载的依据;函数重载是由函数名和参数列表决定的。
A.函数默认参数 和 函数重载结合在一起
int f(int aa, int b,int c=0) //默认函数参数 { return aa + b; } int f(int aa, int b) { return aa + b; }
f(1, 2);=》当函数默认参数 和 函数重载结合在一起时,会发生二义性错误,无法匹配。
B.函数重载 和 函数指针 结合
//函数重载 int func(int x) { return x; } int func(int a, int b) { return a + b; } int func(const char* s) { return strlen(s); } //函数指针 typedef int(*PFUNC)(int); // int(int a) int main() { int c = 0; PFUNC p = NULL; //定义一个函数指针 变量 ,用来指向函数的入口地址 p = func; c = p(1); //调用函数指针变量 printf("c = %d\n", c); return 0; }
因为函数指针在声明时,限定了参数类型和参数个数,所以在调用函数指针变量时会严格检查!
C.杂
(1)普通函数形参为非引用类型,非指针类型,形参一个带const,一个不带const1 void print(int x);
2 void print(const int x); //不算重载,直接报错重定义
(2)普通函数形参为引用类型或指针类型,一个形参带const,一个不带const
1 void print(int *x);
2 void print(const int *x); //算重载,执行正确,实参为const int *时候调用这个,为int*的时候调用上面一个
3 void print(int &x);
4 void print(const int &x); //算重载,执行正确,实参为const int &时候调用这个,为int&的时候调用上面一个
(3)类的成员函数,形参完全相同,一个函数为const成员函数,一个函数为普通成员函数
1 void print();
2 void print() const; //算重载。const对象或const引用或const指针调用时调用这个函数,普通对象或普通引用调用时调用上面一个。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 如何组织构建多文件 C 语言程序(二)
- 关于指针的一些事情
- 如何写好 C main 函数
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- php7 扩展类的写法[2]
- php7 类的方法传参[3]
- php7 读取php.ini[4]
- C#、ASP.NET通用扩展工具类之TypeParse
- Lua编程示例(二):面向对象、metatable对表进行扩展
- Windows Powershell扩展类型系统
- Lua中调用C++函数示例
- Lua和C语言的交互详解
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C#、ASP.NET通用扩展工具类之LogicSugar
- SQL Server下几个危险的扩展存储过程
- C++联合体转换成C#结构的实现方法
- jQuery 学习第七课 扩展jQuery的功能 插件开发