C++基础01----->C++对C的扩展
C++对C的拓展:
1、C++helloworld(cout、cin)
2、面向对象helloworld(类、对象、成员函数)
3、为什么要引入成员函数
4、新增“namespace”
5、增强实用性(变量定义)
6、增强“register”
7、增强“检测”
8、增强“struct”
9、新增类型“bool”
10、增强三目运算符
11、增强“const”
12、新增“引用”
13、函数增强
1、C++helloworld(cout、cin)
C语言中:用printf。
1 #include<iostream> 2 using namespace std; 3 4 void main() 5 { 6 //c 7 printf("请输入一个数字:\n"); 8 9 //c++ 10 11 //cout 标准输出 代表黑屏幕<--代码; 12 //endl--换行 13 //cin 标准输入 代表黑屏幕——>代码 14 double num; 15 cout << "请输入一个数字:"<<endl; 16 cin >> num; 17 cout << "获取到的数字是:" << num << endl; 18 19 system("pause"); 20 }
2、面向对象helloworld(类、对象、成员函数)
C语言中struct:没有成员函数。
抛砖: c++编译器是如何处理 多个对象,调用类的成员函数的;具体一点:如何区分是c1 c2 c3 调用了getS?
1 //类是一个数据类型,(固定大小内存块的别名); 定义一个类,是一个抽象的概念,不会给你分配内存 2 //用数据类型定义变量的时候,才会分配内存 3 class MyCicle 4 { 5 public: 6 double m_s; //圆的面积 属性 成员变量 7 double m_r; //圆的半径 8 9 public: 10 void setR(double r) //成员函数 11 { 12 m_r = r; 13 } 14 15 double getR() //成员函数 16 { 17 return m_r; 18 } 19 double getS() 20 { 21 22 m_s = 3.14*m_r*m_r; 23 return m_s; 24 } 25 }; 26 void main() 27 { 28 MyCicle c1, c2, c3; 29 double r=0; 30 double s = 0; 31 cout << "请输入圆的半径:"; 32 cin >> r; 33 c1.setR(r); 34 cout << "圆的面积是:" << c1.getS() << endl; 35 system("pause"); 36 }
3、为什么要引入成员函数
1 class circle 2 { 3 public: 4 double r; 5 double pi = 3.1415926; 6 double area = pi * r*r; 7 }; 8 9 // 2010编译不通过 但是在2013编译器能编译通过 10 int main() 11 { 12 circle c1; 13 cout << "please input your r" << endl; 14 cin >> c1.r; 15 16 cout << c1.area << endl; //乱码 17 18 system("pause"); 19 return 0; 20 }
分析:
4、新增“namespace”
1)在C++中,名称(name)可以是符号常量、变量、宏、函数、结构、枚举、类和对象等等。为了避免,在大规模程序的设计中,以及在程序员使用各种各样的C++库时,这些标识符的命名发生冲突,标准C++引入了关键字namespace(命名空间/名字空间/名称空间/名域),可以更好地控制标识符的作用域。
2)std是c++标准命名空间,c++标准程序库中的所有标识符都被定义在std中,比如标准库中的类iostream、vector等都定义在该命名空间中。
3) 当使用<iostream.h>时,相当于在c中调用库函数,使用的是全局命名空间,也就是早期的c++实现;
4) 当使用<iostream>的时候,该头文件没有定义全局命名空间,必须使用namespace std;这样才能正确使用cout。若不引入using namespace std ,需要这样做。std::cout。
1 #include<iostream> 2 using namespace std; 3 4 //1文件中iostream 没有引入标准的 std ; 需要我们程序员手工的写 5 //2 如果不写 using namespace std; 需要显示的引入std 6 7 //void main() 8 //{ 9 // std::cout << "namespace test" << std::endl; 10 // 11 // system("pause"); 12 //} 13 14 //3 定义命名空间 15 namespace namespaceA 16 { 17 int a = 10; 18 } 19 namespace namespaceB 20 { 21 int a = 20; 22 namespace namespaceC 23 { 24 struct Teacher 25 { 26 char name[32]; 27 int age; 28 }; 29 } 30 } 31 //4 使用命名空间 32 // 33 void main() 34 { 35 using namespace namespaceA; 36 using namespace namespaceB; 37 cout << namespaceA::a << endl; 38 cout << namespaceB::a << endl; 39 //方法1 40 namespaceB::namespaceC::Teacher t1; 41 t1.age = 33; 42 cout << t1.age << endl; 43 //方法2: 44 using namespaceB::namespaceC::Teacher; 45 Teacher t2; 46 t2.age = 36; 47 cout << t2.age << endl; 48 49 system("pause"); 50 }
5、增强实用性(变量定义)
C语言中的变量都必须在作用域开始的位置定义!!
C++中更强调语言的“实用性”,所有的变量都可以在需要使用时再定义。
1 #include<iostream> 2 using namespace std; 3 void main() 4 { 5 int i; 6 printf("hello...\n"); 7 8 int k;//C语言中k在这里定义式不合法的! 9 10 i = 10; 11 k = 11; 12 printf("i:%d k:%d \n", i, k); 13 system("pause"); 14 }
6、增强“register”
register关键字:请求“编译器”将局部变量存储于寄存器中,速度快。
在c语言中: register修饰的变量 不能取地址。
在C++中:依然支持register关键字,C++编译器有自己的优化方式,不使用register也可能做优化。
C++中可以取得register变量的地址,C++编译器发现程序中需要取register变量的地址时,register对变量的声明变得无效。
1 void main() 2 { 3 register int a = 0; 4 printf("&a: %d \n", &a); //不能在寄存器变量上取地址 5 6 for (int i = 0; i < 1000; i++) //不使用register也可能做优化 7 { 8 printf("i:%d \n", i); 9 } 10 system("pause"); 11 }
7、增强“检测”(同名变量、类型检测)
1、同名变量检测:
在C语言中,重复定义多个同名的全局变量是合法的
在C++中,不允许定义多个同名的全局变量
C语言中多个同名的全局变量最终会被链接到全局数据区的同一个地址空间上
int g_var;
int g_var = 1;
C++直接拒绝这种二义性的做法。
2、类型检测
C语言中:函数f的返回值是什么类型、参数又是什么类型、函数g可以接受多少个参数,都没有很严格的规范
C++中:所有的变量和函数都必须有类型。C语言中的默认类型在C++中是不合法的。
1 #include <stdlib.h> 2 #include <string.h> 3 #include <stdio.h> 4 5 f(a) 6 { 7 printf("a = %d\n", a); 8 } 9 g() 10 { 11 return 5; 12 } 13 int main() 14 { 15 f(10); 16 printf("g() = %d\n", g(1, 2, 3, 4, 5)); 17 system("pause"); 18 return 0; 19 20 }
在C语言中:
int f( );表示返回值为int,接受任意参数的函数
int f(void);表示返回值为int的无参函数
在C++中:
int f( );和int f(void)具有相同的意义,都表示返回值为int的无参函数
8、增强“struct”
C语言中:struct定义了一组变量的集合,C编译器并不认为这是一种新的类型,使用时:struct Teacher t1;
C++中:struct是一个新类型的定义声明。使用时:Teacher t1;(不需要加struct)
1 struct Teacher 2 { 3 int age; 4 }; 5 void main() 6 { 7 Teacher t1; 8 t1.age = 10; 9 cout << "t1.age:" << t1.age << endl; 10 system("pause"); 11 }
struct 关键字 class关键字 完成的功能是一样的
区别后面介绍 抛砖
1 class c1 2 { 3 public: 4 protected: 5 private: 6 }; 7 struct Teacher 8 { 9 public: 10 char name[32]; 11 int age; 12 protected: 13 int a; 14 };
9、新增类型“bool”
C++中的bool可取的值只有true和false
bool只占用一个字节
1 bool b1 = true; //告诉c++编译器给我分配 1个字节的内存 2 bool b2, b3, b4, b5; 3 // 4 cout << "sizeof(bool)" << sizeof(bool) << endl; 5 6 //bool变量 1 或者 0 7 b1 = 10; 8 cout << "bl:" << b1 << endl; 9 10 //bool变量 1 或者 0 11 b1 = -10; 12 cout << "bl:" << b1 << endl; 13 14 //bool变量 1 或者 0 15 b1 = 0; 16 cout << "bl:" << b1 << endl;
10、增强三目运算符
1)C语言返回变量的值 C++语言是返回变量本身
C语言中:三目运算符返回的是变量值,不能作为左值使用
C++中:三目运算符可直接返回变量本身,因此可以出现在程序的任何地方
注意:三目运算符可能返回的值中如果有一个是常量值,则不能作为左值使用。(a < b ? 1 : b )= 30;
2) 如何做到的
让表达式返回一个内存空间 ..内存首地址 指针
在C语言中 如何 实现 c++的效果
3)本质
c++编译器 帮我们程序员完成了 取地址的工作
1 int main() 2 { 3 int a = 10; 4 int b = 20; 5 6 //返回一个最小数 并且给最小数赋值成3 7 //三目运算符是一个表达式 ,表达式不可能做左值 8 (a < b ? a : b) = 30; 9 //int z = (a < b ? a : b ); 10 11 //C++如何在C基础上做到的 12 //实质:c++编译器 帮我们程序员完成了取内存空间地址的工作 13 //*(a < b ? &a : &b) = 30; 14 15 printf("a = %d, b = %d\n", a, b); 16 system("pause"); 17 return 0; 18 }
11、增强“const”
1)const基础知识
1 #include<iostream> 2 using namespace std; 3 4 void main() 5 { 6 const int a=1 ; 7 int const b=2; 8 9 const int* c=(int*)&a;//const修饰的是指针所指向的内存空间不能被修改,但指针变量本身可修改 10 int* const d= (int*)&b; 11 const int* const e= (int*)&b; 12 13 c = (int*)&b; 14 *d = 3; 15 16 cout << b << endl; 17 cout << *c << endl; 18 cout << *d << endl; 19 cout << *e << endl; 20 system("pause"); 21 }
复杂数据类型
1 struct Teacher 2 { 3 char name[64]; 4 int age=18; 5 }; 6 7 //指针所指向的内存空间,不能被修改 8 int operatorTeacher01(const Teacher *pT) 9 { 10 //pT->age = 10;//不能修改 11 return pT->age; 12 } 13 14 //指针变量本身不能被修改 15 int operatorTeacher02(Teacher * const pT) 16 { 17 pT->age = 10; 18 //pT = NULL; //no 19 return pT->age; 20 } 21 22 int operatorTeacher03(const Teacher * const pT) 23 { 24 //pT->age = 10; no 25 //pT = NULL; //no 26 return pT->age; 27 } 28 29 void main() 30 { 31 Teacher t1; 32 cout<<operatorTeacher01(&t1)<<endl; 33 cout << operatorTeacher02(&t1) << endl; 34 cout << operatorTeacher03(&t1) << endl; 35 system("pause"); 36 }
2)C语言中:const是一个冒牌货,
C++语言中:const是一个真正的常量(符号表机制)
1 void main() 2 { 3 const int a = 10; 4 //a = 11; no 5 6 //C++中const是一个真正的常量 7 //在C中只要取地址再复制依然可以修改const变量的值 8 9 int* p = (int*)&a; 10 *p = 20; 11 cout << a << endl; 12 cout << *p << endl; 13 14 /*解释: 15 C++编译器对const常量的处理 16 当碰见常量声明时,在符号表中放入常量 = è问题:那有如何解释取地址 17 编译过程中若发现使用常量则直接以符号表中的值替换 18 编译过程中若发现对const使用了extern或者&操作符,则给对应的常量分配存储空间(兼容C) 19 */ 20 system("pause"); 21 22 }
const分配内存的时机 编译器编译器期间
1 //如何证明:b的地址是在a、c中间 2 void main() 3 { 4 int a; 5 const int b=10; 6 int c; 7 cout << "&a=" << &a << endl; 8 cout << "&a=" << &b << endl; 9 cout << "&a=" << &c << endl; 10 system("pause"); 11 }
3)const 和 #define的相同与区别
const 和 #define的相同之处:都能处理array[a+b]语法
1 #define d 20 2 void main() 3 { 4 int a = 10; 5 int b = 20; 6 //int array[a + b];//linux内核里面是成立的;原因 编译linux内核的gcc编译器支持. 7 //c和c++编译器 不支持这种语法现象 8 9 const int c = 10; 10 //const int d = 20; 11 int array[c + d]; 12 system("pause"); 13 }
const 和 #define的区别
const常量是由编译器处理的,提供类型检查和作用域检查
宏定义由预处理器处理,单纯的文本替换
1 void fun1() 2 { 3 #define a 20 // 即使main不跑fun1,#define都会被预处理器处理 4 const int b = 20;//此时b的作用域是在fun1里面,const常量是会被编译器检查的 5 //#undef a 6 //# undef 7 } 8 9 void fun2() 10 { 11 printf("a = %d\n", a); 12 //printf("b = %d\n", b); 13 } 14 15 int main() 16 { 17 //fun1(); 18 fun2(); 19 system("pause"); 20 return 0; 21 }
结论:
C语言中const变量是只读变量,有自己的存储空间
C++中的const常量可能分配存储空间,也可能不分配存储空间
当const常量为全局,并且需要在其它文件中使用
当使用&操作符取const常量的地址
12、新增“引用”
1)引用的基础知识
一、引用的概念:
- 对一段连续的内存空间可以取多个别名,
引用可以看作是一个已定义变量的别名,也就是给内存空间起别名 - 注意:普通引用必须初始化
1 #include<iostream> 2 using namespace std; 3 4 void main() 5 { 6 int a = 10; 7 8 //引用的语法:Type& name = var; 9 10 int &b = a;//与a共用一个内存空间 11 //int &b;//普通引用 必须要初始化 12 13 b = 100;//相当于把a修改成100了 14 cout << a << endl; 15 cout << b << endl; 16 17 a = 200; 18 cout << a << endl; 19 cout << b << endl; 20 system("pause"); 21 }
二、引用是C++的概念,不能用c语言的语法去思考它
- 思考1:C++编译器背后做了什么工作?
1 int main() 2 { 3 int a = 0; 4 int &b = a; //int * const b = &a (C++编译器自动取a的地址) 5 b = 11; //*b = 11;(用到b时C++编译器自动变成*b) 6 7 return 0; 8 }
三、引用作函数参数:(引用作为函数参数声明时不用进行初始化)
- 基础数据类型的引用
1 //基础类型的引用 2 void myswap(int a, int b) 3 { 4 int c = 0; 5 c = a; 6 a = b; 7 b = c; 8 } 9 void myswap02(int *a, int *b) 10 { 11 int c = 0; 12 c = *a; 13 *a = *b; 14 *b = c; 15 } 16 // 引用作为函数参数声明时不进行初始化 17 void myswap03(int &a, int &b) 18 { 19 int c = 0; 20 c = a; 21 a = b; 22 b = c; 23 } 24 void main() 25 { 26 int x, y; 27 x = 10; 28 y = 20; 29 30 myswap(x, y); 31 cout << x << y << endl; 32 33 myswap02(&x, &y); 34 cout << x << y << endl; 35 36 //a就是x的别名 b就是y的别名 37 myswap03(x, y); 38 cout << x << y << endl; 39 40 //int &c ; //普通引用 必须要初始化 41 system("pause"); 42 }
- 复杂数据类型的引用
1 //复杂数据类型 的引用 2 struct Teacher 3 { 4 char name[64]; 5 int age; 6 }; 7 8 void printfT(Teacher *pT) 9 { 10 cout << pT->age << endl; 11 } 12 13 //pT是t1的别名 ,相当于修改了t1 14 void printfT2(Teacher &pT) 15 { 16 //cout<<pT.age<<endl; 17 pT.age = 33; 18 } 19 20 //pT和t1的是两个不同的变量 21 void printfT3(Teacher pT) 22 { 23 pT.age = 45; //只会修改pT变量 ,不会修改t1变量 24 cout << pT.age << endl; 25 } 26 void main() 27 { 28 Teacher t1; 29 t1.age = 35; 30 31 printfT(&t1); 32 33 printfT2(t1); //pT是t1的别名 34 cout << t1.age << endl; //33 35 36 printfT3(t1);// pT是形参 ,t1 copy一份数据 给pT //---> pT = t1 37 cout << t1.age << endl;//33 38 39 system("pause"); 40 return; 41 }
2)引用的本质
- 第一点 单独定义的引用时,必须初始化;说明很像一个常量
1 void main() 2 { 3 4 const int c = 10; 5 6 int a = 10; 7 int &b = a; //b很像一个常量 8 //b是a的别名,请问c++编译器后面做了什么工作? 9 b = 11; 10 11 cout << "a:" << a << endl; 12 cout << "b:" << b << endl; 13 cout << "&a:" << &a << endl; 14 cout << "&b:" << &b << endl; //===> a 和 b就是同一块内存空间的门牌号 15 16 system("pause"); 17 return; 18 }
- 普通引用有自己的空间吗? 有
1 //2 普通引用有自己的空间吗? 有 2 struct Teacher 3 { 4 char name[64]; //64 5 int age; //4 6 7 int &a; //4 0 //很像指针 所占的内存空间大小 8 int &b; //4 0 9 }; 10 11 void main() 12 { 13 cout << sizeof(Teacher) << endl; 14 system("pause"); 15 }
- 引用的本质:
一、引用在C++中的内部实现是一个常指针
Type& name====Type* const name
二、C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同。
间接赋值成立的三个条件的角度:
1定义两个变量 (一个实参一个形参)
2建立关联 实参取地址传给形参
3*p形参去间接的修改实参的值
-
进一步,引用的结论:
一、引用在实现上,只不过是把:间接赋值成立的三个条件的后两步和二为一
当实参传给形参引用的时候,只不过是c++编译器帮我们程序员手工取了一个实参地址,传给了形参引用(常量指针)
二、当我们使用引用语法的时,我们不去关心编译器引用是怎么做的
当我们分析奇怪的语法现象的时,我们才去考虑c++编译器是怎么做的。
3)引用做函数返回值(左值右值都可以当)
返回引用是基础类型:
- 若返回栈变量,不能成为其它引用的初始值,不能作为左值使用
1 #include<iostream> 2 using namespace std; 3 4 int getAA1() 5 { 6 int a; 7 a = 10; 8 return a; 9 } 10 11 //返回a的本身 返回a的一个副本 10 12 int& getAA2() 13 { 14 int a; //如果返回栈上的 引用, 有可能会有问题 15 a = 10; 16 return a; 17 } 18 19 int* getAA3() 20 { 21 int a; 22 a = 10; 23 return &a; 24 } 25 26 void main() 27 { 28 int a1 = 0; 29 int a2 = 0; 30 a1 = getAA1();//返回的是a的值 31 32 a2 = getAA2(); //10 返回的也是a的值,并非a的地址 33 int &a3 = getAA2(); //若返回栈变量 不能成为其它引用的初始值 34 //一执行取地址的操作就有问题 乱码 35 //getAA2()=11;//不能当左值 36 37 int* a4 = getAA3();//乱码 38 39 cout << a1 << endl; 40 cout << a2 << endl; 41 cout << a3 << endl; 42 cout << *a4 << endl; 43 system("pause"); 44 }
- 若返回静态变量或全局变量,可以成为其他引用的初始值,即可作为右值使用,也可作为左值使用
C++链式编程中,经常用到引用,运算符重载专题
1 //变量是static 或者是 全局变量 2 int j1() 3 { 4 static int a = 10; 5 a++; 6 return a; 7 } 8 9 int& j2() 10 { 11 static int a = 10; 12 a++; 13 return a; 14 } 15 //若返回静态变量或全局变量 16 // 可以成为其他引用的初始值 17 // 即可作为右值使用,也可作为左值使用 18 void main() 19 { 20 int a1 = 10; 21 int a2 = 20; 22 //当右值 23 a1 = j1(); 24 a2 = j2(); 25 int &a3 = j2(); 26 27 //当左值: 28 j2() = 100; 29 30 cout << a1 << endl;//11 31 cout << a2 << endl;//11 32 cout << a3<< endl;//12 33 34 system("pause"); 35 }
返回引用是非基础类型(结构体、类),后面讲
4)指针引用
用法:int* &myp=地址;(可以用来代替二级指针)
1 #include "iostream" 2 using namespace std; 3 4 // 5 struct Teacher 6 { 7 char name[64]; 8 int age; 9 }; 10 11 //在被调用函数 获取资源 12 int getTeacher(Teacher **p)//二级指针用来接一级指针的地址 13 { 14 Teacher *tmp = NULL; 15 if (p == NULL) 16 { 17 return -1; 18 } 19 tmp = (Teacher *)malloc(sizeof(Teacher)); 20 if (tmp == NULL) 21 { 22 return -2; 23 } 24 tmp->age = 33; 25 // p是实参的地址 *实参的地址 去间接的修改实参的值 26 *p = tmp; 27 } 28 29 30 //指针的引用 做函数参数 31 int getTeacher2(Teacher* &myp) 32 { 33 //给myp赋值 相当于给main函数中的pT1赋值 34 myp = (Teacher *)malloc(sizeof(Teacher)); 35 if (myp == NULL) 36 { 37 return -1; 38 } 39 myp->age = 36; 40 } 41 42 void FreeTeacher(Teacher *pT1) 43 { 44 if (pT1 == NULL) 45 { 46 return; 47 } 48 free(pT1); 49 } 50 51 void main() 52 { 53 Teacher *pT1 = NULL; 54 55 //1 c语言中的二级指针 56 getTeacher(&pT1); 57 cout << "age:" << pT1->age << endl; 58 FreeTeacher(pT1); 59 60 //2 c++中的引用 (指针的引用) 61 //引用的本质 间接赋值后2个条件 让c++编译器帮我们程序员做了。 62 getTeacher2(pT1); 63 64 cout << "age:" << pT1->age << endl; 65 FreeTeacher(pT1); 66 67 system("pause"); 68 }
5)常引用
Const & int e 相当于 const int * const e
13、函数增强