C++ 对C的扩展 day2

1.1 C++对C语言的加强

  1.1.1 namespace 命名空间

  所谓 namespace,是指标识符的各种可见范围C++标准程序库中的所有标识符都被定义于一个名为 std namespace 中。
    一 : <iostream><iostream.h>格式不一样,前者没有后缀,实际上,在你的编译器include文件夹里面可以看到,
    二者是两个文件,打开文件就会发现,里面的代码是不一样的。后缀.h 的头文件 c++标准已经明确提出不支持了
    早些的实现将标准库功能定义在全局空间里,声明在带.h 后缀的头文件里, c++标准为了和 C 区别开,
    也为了正确使用命名空间,规定头文件不使用后缀.h。 因此,
      1)当使用<iostream.h>时,相当于在 c 中调用库函数,使用的是全局命名空间,也就是早期的 c++实现;
      
2当使用<iostream>的时候,该头文件没有定义全局命名空间,必须使用 namespace std这样才能正确使用 cout
   二: 由于 namespace 的概念,使用 C++标准程序库的任何标识符时,可以有三种选择:
    1、直接指定标识符。例如 std::ostream 而不是 ostream。完整语句如下: std::cout <<std::hex << 3.4 << std::endl;
    2
、使用 using 关键字。 using std::cout; using std::endl; using std::cin; 以上程序可以写成cout << std::hex << 3.4 << endl;
    3、最方便的就是使用 using namespace std; 例如: using namespace std;这样命名空间std 内定义的所有标识符都有效(曝光)。就好像它们被声明为全局变量一样。

  1.1.2 C++命名空间的定义与使用

  std c++标准命名空间, c++标准程序库中的所有标识符都被定义在 std 中,比如标准库中的类 iostreamvector等都定义在该命名空间中,
  使用时要加上 using 声明(using namespace std) using 指示(std::stringstd::vector<int>).
  

  C 中的命名空间
    在 C 语言中只有一个全局作用域
    C 语言中所有的全局标识符共享同一个作用域
    标识符之间可能发生冲突
  C++中提出了命名空间的概念
    命名空间将全局作用域分成不同的部分
    不同命名空间中的标识符可以同名而不会发生冲突
    命名空间可以相互嵌套
    全局作用域也叫默认命名空间

  C++命名空间的定义:
    namespace name { … }    

  C++命名空间的使用:
    使用整个命名空间: using namespace name;
    使用命名空间中的变量: using name::variable;
    使用默认命名空间中的变量: ::variable
    默认情况下可以直接使用默 认命名空间中的所有标识符

  1.1.3 结论

    1) 当使用<iostream>的时候,该头文件没有定义全局命名空间,必须使用 using namespace std;这样才能正确使用 cout。
   若不引入
using namespace std ,需要这样做。 std::cout
    2) c++标准为了和 C 区别开,也为了正确使用命名空间,规定头文件不使用后缀.h。
    3C++命名空间的定义: namespace name { … }
    4
using namespace NameSpaceA;
    5namespce 定义可嵌套。

1.2 实用性增强

  #include "iostream"
  using namespace std;
  //C 语言中的变量都必须在作用域开始的位置定义!!
  //C++中更强调语言的“实用性”,所有的变量都可以在需要使用时再定义。
  int main11()
  {
    int i = 0;
    pri
ntf("ddd");
    int k;
    system("pause");
    return 0;
  }

1.3 register 关键字增强

  //register 关键字 请求编译器让变量 a 直接放在寄存器里面,速度快
  //在 c 语言中 register 修饰的变量 不能取地址,但是在 c++里面做了内容
    register 关键字的变化
    register 关键字请求“编译器”将局部变量存储于寄存器中
    C 语言中无法取得 register 变量地址
    在 C++中依然支持 register 关键字
    C++编译器有自己的优化方式,不使用 register 也可能做优化
    C++中可以取得 register 变量的地址

    C++编译器发现程序中需要取 register 变量的地址时, register 对变量的声明变得无效。
    早期 C 语言编译器不会对代码进行优化,因此 register 变量是一个很好的补充。

  int main()
  {
    register int a = 0;
    printf("&a = %x\n", &a);
    system("pause");
    return 0;
  }

1.4 变量检测增强

  在 C 语言中,重复定义多个同名的全局变量是合法的
  在 C++中,不允许定义多个同名的全局变量
  C 语言中多个同名的全局变量最终会被链接到全局数据区的同一个地址空间上
    int g_var;
    int g_var = 1;
  C++直接拒绝这种二义性的做法。

1.5 struct 类型加强

  struct 类型的加强:
    C语言的 struct 定义了一组变量的集合, C 编译器并不认为这是一种新的类型
    C++中的 struct 是一个新类型的定义声明
  struct Student

  {
    char name[100];
    int age;
  };
  int main(int argc, char *argv[])
  {
    Student s1 = {"wang", 1};
    Student s2 = {"wang2", 2};
    return 0;
  }

1.6 C++中所有的变量跟函数都必须有类型

  C++中所有的变量和函数都必须有类型,C 语言中的默认类型在 C++中是不合法的
  函数 f 的返回值是什么类型,参数又是什么类型?
  函数 g 可以接受多少个参数?
   //更换成.cpp 试试
  f(i)
  {
    printf("i = %d\n", i);
  }
  g()
  {
    return 5;
  }
  int main(int argc, char *argv[])
  {
    f(10);
    printf("g() = %d\n", g(1, 2, 3, 4, 5));

    getchar();
    return 0;
  }

  总结:
  在 C 语言中
    int f( );表示返回值为 int,接受任意参数的函数
    int f(void);表示返回值为 int 的无参函数
  在
C++
    int f( );int f(void)具有相同的意义,都表示返回值为 int 的无参函数
   C++
更加强调类型,任意的程序元素都必须显示指明类型

1.7 新增bool类型

  C++中的布尔类型
  C++在 C 语言的基本类型系统之上增加了 bool
  C++中的 bool 可取的值只有 true 和 false
  理论上 bool 只占用一个字节,
  如果多个 bool 变量定义在一起,可能会各占一个 bit,这取决于编译器的实现
  true 代表真值,编译器内部用 1 来表示
  false 代表非真值,编译器内部用 0 来表示
  bool 类型只有 true(非 0)和 false(0)两个值
  C++编译器会在赋值时将非 0 值转换为 true, 0 值转换为 false

1.8 三元运算符加强

  int main()
  {
    int a = 10;
    int b = 20;
    //返回一个最小数 并且给最小数赋值成 30
    //C语言中 三元运算符是一个表达式 ,表达式不可能做左值
    (a < b ? a : b )= 30;
    printf("a = %d, b = %d\n", a, b);
    system("pause");
    return 0;
   }

  1C 语言返回变量的值 C++语言是返回变量本身
    C 语言中的三目运算符返回的是变量值,不能作为左值使用
    C++中的三目运算符可直接返回变量本身,因此可以出现在程序的任何地方
  2)注意:三目运算符可能返回的值中如果有一个是常量值,则不能作为左值使用(a < b ? 1 : b )= 30;
  3
C 语言如何支持类似 C++的特性呢?
    ====>当左值的条件:要有内存空间; C++编译器帮助程序员取了一个地址而已

 

1.2 C++/C里面的const

1.2.1 const的基础知识

  int main()
  {
    const int a;
    int const b;
    const int *c;
    int * const d;
    const int * const e ;
    return 0;
    }

  Int func1(const )
  初级理解: const 是定义常量==》 const 意味着只读

  含义:
    //第一个第二个意思一样 代表一个常整形数
    //第三个 c 是一个指向常整形数的指针(所指向的内存数据不能被修改,但是本身可以修改)
    //第四个 d 常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)
    //第五个 e 一个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改)

  Const 好处
    //合理的利用 const,
    //1 指针做函数参数,可以有效的提高代码可读性,减少 bug;
    //2 清楚的分清参数的输入和输出特性

  int setTeacher_err( const Teacher *p)
  Const 修改形参的时候,在利用形参不能修改指针所向的内存空间

1.2.2 C中的“冒牌货”

  int main()
  {
    const int a = 10;
    int *p = (int*)&a;
    printf("a===>%d\n", a);
    *p = 11;
    printf("a===>%d\n", a);
    printf("Hello......\n");
    return 0;
   }

  解释:
    C++编译器对 const 常量的处理
    当碰见常量声明时,在符号表中放入常量 =➔问题:那有如何解释取地址
    编译过程中若发现使用常量则直接以符号表中的值替换
    编译过程中若发现对 const 使用了 extern 或者&操作符,则给对应的常量分配存储空间(兼容 C)
    ?联想: int &a = 1(err) & const int &a = 10(ok)?

  注意:
    C++编译器虽然可能为 const 常量分配空间,但不会使用其存储空间中的值。
  结论:
   C 语言中的 const 变量
      C 语言中 const 变量是只读变量,有自己的存储空间
   C++中的 const 常量
      可能分配存储空间,也可能不分配存储空间
      当 const 常量为全局,并且需要在其它文件中使用
      当使用&操作符取 const 常量的地址

1.2.3 const 与 #define的区别   

    C++中的 const 修饰的,是一个真正的常量,而不是 C 中变量(只读)。在 const 修饰的常量编译期间,就已经确定下来了。

 对比加深
    C++中的 const 常量类似于宏定义
    const int c = 5; ≈ #define c 5
    C++中的 const 常量与宏定义不同
    const 常量是由编译器处理的,提供类型检查和作用域检查
    宏定义由预处理器处理,单纯的文本替换
    //在 func1 定义 a,在 func2 中能使用吗?
    //在 func1 中定义的 b,在 func2 中能使用吗?
  练习
    void fun1()
    {
      #define a 10
      const int b = 20;
      //#undef a # undef
    }
    void fun2()
    {
      printf("a = %d\n", a);
      //printf("b = %d\n", b);
    }
    int main()
    {
      fun1();
      fun2();
      return 0;
    }    

  结论:

    C 语言中的 const 变量
    C 语言中 const 变量是只读变量,有自己的存储空间
    C++中的 const 常量
    可能分配存储空间
,也可能不分配存储空间
    当
const 常量为全局,并且需要在其它文件中使用,会分配存储空间
    当使用
&操作符,取 const 常量的地址时,会分配存储空间
    当
const int &a = 10; const 修饰引用时,也会分配存储空间

1.3 引用专题讲座

1.3.1 引用的概念

  a) 在C++中新增加了引用的概念
  b) 引用可以看作一个已定义变量的别名
  c) 引用的语法: Type& name = var;
  d) 引用做函数参数那?(引用作为函数参数声明时不进行初始化)

  void main01()
  {
    int a = 10; //c编译器分配4个字节内存。。。 a内存空间的别名
    int &b = a; //b就是a的别名。。。
    a =11; //直接赋值
  {
    int *p = &a;
    *p = 12;
    printf("a %d \n",a);
   }
    b = 14;
    printf("a:%d b:%d", a, b);
    system("pause");
  }

1.3.2 引用是C++的概念

  属于C++编译器对C的扩展
  问题: C中可以编译通过吗?
  int main()
  { 
    int a = 0;
    int &b = a; //int * const b = &a
    b = 11; //*b = 11;
    return 0;
  }
  结论:请不要用C的语法考虑 b=11

1.3.3 引用做函数参数

   普通引用在声明时必须用其它的变量进行初始化,
   引用作为函数参数声明时不进行初始化
   //05复杂数据类型 的引用
   struct Teacher
   {
    char name[64];
    int age ;
   };
   void printfT(Teacher *pT)
   {
    cout<<pT->age<<endl;
   }
   //pT是t1的别名 ,相当于修改了t1
   void printfT2(Teacher &pT)
   {
    //cout<<pT.age<<endl;
    pT.age = 33;
   }
   //pT和t1的是两个不同的变量
   void printfT3(Teacher pT)
   {
    cout<<pT.age<<endl;
    pT.age = 45; //只会修改pT变量 ,不会修改t1变量
   }
   void main()
   {
    Teacher t1;
    t1.age = 35;
    printfT(&t1);
    printfT2(t1); //pT是t1的别名
    printf("t1.age:%d \n", t1.age); //33
    printfT3(t1) ;// pT是形参 ,t1 copy一份数据 给pT //---> pT = t1
    printf("t1.age:%d \n", t1.age); //35
    cout<<"hello..."<<endl;
    system("pause");
    return ;
   }

1.3.4 引用的意义

  1)引用作为其它变量的别名而存在,因此在一些场合可以代替指针
  2)引用相对于指针来说具有更好的可读性和实用性

 

 

1 .3. 5  引用本质的思考

  思考1: C++编译器背后做了什么工作?
  int main()
  {
    int a = 10;
    int &b = a;
    //b是a的别名,请问c++编译器后面做了什么工作?
    b = 11;
    cout<<"b--->"<<a<<endl;
    printf("a:%d\n", a);
    printf("b:%d\n", b);
    printf("&a:%d\n", &a);
    printf("&b:%d\n", &b); //请思考:对同一内存空间可以取好几个名字吗?
    system("pause");
    return 0;
   }
  单独定义的引用时,必须初始化;说明很像一个常量

  思考2:普通引用有自己的空间吗?
  struct Teacer {
      int &a;
      int &b;
   };
   int main()
   {
      printf("sizeof(Teacher) %d\n", sizeof(Teacer));
      system("pause");
      return 0;
   }
   引用是一个有地址,引用是常量。。。。。
   char *const p

 

 

1.3.6 引用本质

  1)引用在C++中的内部实现是一个常指针
      Type& name ➔Type* const name
  2
C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同。
  3)从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间。这是C++为了实用性而做出的细节隐藏

 

 

   Int main()
  {
    int x = 10;
    func(x);
  }

  4) 请仔细对比间接赋值成立的三个条件

  1.  定义两个变量 (一个实参一个形参)
  2.  建立关联 实参取地址传给形参
  3.  *p形参去间接的修改实参的值

 

1.3.7 引用结论

  1)引用在实现上,只不过是把: 间接赋值成立的三个条件的后两步和二为一
    //当实参传给形参引用的时候,只不过是c++编译器帮我们程序员手工取了一个实参地址,传给了形参引用(常量指针)
  2)当我们使用引用语法的时,我们不去关心编译器引用是怎么做的
    当我们分析奇怪的语法现象的时,我们才去考虑c++编译器是怎么做的

1.3.8 函数返回值是引用

  C++引用使用时的难点:
    当函数返回值为引用时
  若返回栈变量:  
    不能成为其它引用的初始值
    不能作为左值使用
  若返回静态变量或全局变量:
    可以成为其他引用的初始值
    即可作为右值使用,也可作为左值使用
  C++链式编程中,经常用到引用,运算符重载专题   

 

  返回值是基础类型,当引用
   int getAA1()
   {
     int a;
     a = 10;
     return a;
   }
   //基础类型a返回的时候,也会有一个副本
   int& getAA2()
   {
     int a;
     a = 10;
     return a;
   }
   int* getAA3()
   {
     int a;
     a = 10;
     return &a;
   }

  

  返回值是static变量,当引用
  //static修饰变量的时候,变量是一个状态变量
  int j()
  {
    static int a = 10;
    a ++;
    printf("a:%d \n", a);
    return a;
  }
  int& j1()
  {
   static int a = 10;
   a ++;
   printf("a:%d \n", a);
   return a;
  }
  int *j2()
  {
   static int a = 10;
   a ++;
   printf("a:%d \n", a);
   return &a;
  }
  {
   // j()的运算结果是一个数值,没有内存地址,不能当左值。。。。。
   //11 = 100;
   //*(a>b?&a:&b) = 111;
   //当被调用的函数当左值的时候,必须返回一个引用。。。。。
   j1() = 100; //编译器帮我们打造了环境
   j1();
   *(j2()) = 200; //相当于我们程序员手工的打造 做左值的条件
   j2();
   system("pause");
  }

  返回值是形参,当引用
  int g1(int *p)
  {  
    *p = 100;
    return *p;
  }
  int& g2(int *p) //
  {
    *p = 100;
    return *p;
  }
  //当我们使用引用语法的时候 ,我们不去关心编译器引用是怎么做的
  //当我们分析乱码这种现象的时候,我们才去考虑c++编译器是怎么做的。。。。
  void main23()
  {
    int a1 = 10;
    a1 = g2(&a1);
    int &a2 = g2(&a1); //用引用去接受函数的返回值,是不是乱码,关键是看返回的内存空间是不是被编译器回收了。。。。
    printf("a1:%d \n", a1);
    printf("a2:%d \n", a2);
    system("pause");
  }

1.3.9 指针引用

   #include "iostream"
    using namespace std;
    struct Teacher
    {
     char name[64];
     int age;
    };
    int getTe(Teacher **myp )
    {
     Teacher *p = (Teacher *)malloc(sizeof(Teacher));
     if (p ==NULL)
     {
      return -1;
     }
     memset(p, 0, sizeof(Teacher));
     p->age = 33;
     *myp = p; //
     return 0;
    }
    //指针的引用而已
    int getTe2(Teacher* &myp)
    {
     myp = (Teacher *)malloc(sizeof(Teacher));
     myp->age = 34;
     return 0;
    }
    void main333()
    {
     Teacher *p = NULL;
     //getTe(&p);
     getTe2(p);
     printf("age:%d \n", p->age);
     system("pause");
    }

1.4 常引用

1.4.1 使用变量初始化const引用

 思考cost int &a = b PK const int &a = 10;  ????问题: const引用,
  在 C++中可以声明 const 引用
  const Type& name = var;
  const 引用让变量拥有只读属性

  案例1:
  int main()
  {
     int a = 10;
     const int &b = a;
     //int *p = (int *)&b;
     b = 11; //err
     //*p = 11; //只能用指针来改变了
     cout<<"b--->"<<a<<endl;
     printf("a:%d\n", a);
     printf("b:%d\n", b);
     printf("&a:%d\n", &a);
     printf("&b:%d\n", &b);
     system("pause");
     return 0;
  }
  案例2:
  void main41()
  {
     int a = 10;
     const int &b = a; //const引用 使用变量a初始化
     a = 11;
     //b = 12; //通过引用修改a,对不起修改不了
     system("pause");
  }
   struct Teacher1
   {
      char name[64];
      int age;
   };
  }
  void printTe2(const Teacher1 *const pt)
  { }
  //const引用让变量(所指内存空间)拥有只读属性
  void printTe(const Teacher1 &t)
  {
     //t.age = 11;
  }
  void main42()
  {
     Teacher1 t1;
     t1.age = 33;
     printTe(t1);
     system("pause");
  }

1.4.2 使用字面量常量初始化 const 引用
  思考:
  1、用变量对const引用初始化, const引用分配内存空间了吗?
  2、用常量对const引用初始化, const引用分配内存空间了吗?
  void main()
  {
    const int b = 10;
    printf("b:%d", &b);
    //int &a1 = 19; 如果不加const编译失败
    const int &a = 19;
    printf("&a:%d \n", &a);
    system("pause");
  }  

1.4.3 综合案例

 void main()
  {
     //普通引用
     int a = 10;
     int &b = a;
     //常量引用 :让变量引用只读属性
     const int &c = a;
     //常量引用初始化 分为两种
     //1 用变量 初始化 常量引用
     {
      int x = 20;
      const int& y = x;
      printf("y:%d \n", y);
     }
     //2 用常量 初始化 常量引用
     {
      //int &m = 10; //引用是内存空间的别名 字面量 10 没有内存空间 没有方法做引用
      const int &m = 10;
     }
     cout<<"hello..."<<endl;
     system("pause");
     return ;
  }

1.4.4 const引用结论

  1) Const & int e 相当于 const int * const e
  2)普通引用 相当于 int *const e1
  3)当使用常量(字面量)对const引用进行初始化时, C++编译器会为常量值分配空间,并将引用名作为这段空间的别名
  4)使用字面量对const引用初始化后,将生成一个只读变量  

 

1.5 C++对 C 的函数扩展

1.5.1 inline 内联函数

  C++中的 const 常量可以替代宏常数定义,如:
    const int A = 3; #define A 3
    C++中是否有解决方案替代宏代码片段呢?(替代宏代码片段就可以避免宏的副作用!)
    C++中推荐使用内联函数替代宏代码片段
    C++中使用 inline 关键字声明内联函数
    内联函数声明时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);
      system("pause");
      return 0;
    }

  说明 1:
    必须 inline int myfunc(int a, int b)和函数体的实现,写在一块
  说明 2
    C++编译器可以将一个函数进行内联编译
    被 C++编译器内联编译的函数叫做内联函数

    内联函数在最终生成的代码中是没有定义的
    C++编译器直接将函数体插入在函数调用的地方
    内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)

  说明 3:
     C++编译器不一定准许函数的内联请求!
  说明 4:
     内联函数是一种特殊的函数,具有普通函数的特征(参数检查,返回类型等)
     内联函数是对编译器的一种请求,因此编译器可能拒绝这种请求
     内联函数由 编译器处理,直接将编译后的函数体插入调用的地方
     宏代码片段 由预处理器处理, 进行简单的文本替换,没有任何编译过程
  说明 5:
     现代 C++编译器能够进行编译优化,因此一些函数即使没有 inline 声明,也可能被编译器内联编译
      另外,一些现代 C++编译器提供了扩展语法,能够对函数进行强制内联
     如: g++中的__attribute__((always_inline))属性
  说明 6:
     C++中内联编译的限制:
     不能存在任何形式的循环语句
     不能存在过多的条件判断语句
     函数体不能过于庞大
     不能对函数进行取址操作
     函数内联声明必须在调用语句之前
     编译器对于内联函数的限制并不是绝对的,内联函数相对于普通函数的优势只是省去了函数调用时压栈,跳转和返回的开销。  
     因此,当函数体的执行开销远大于压栈,跳转和返回所用的开销时,那么内联将无意义。
  结论:
   1) 内联函数在编译时直接将函数体插入函数调用的地方
   2) inline 只是一种请求,编译器不一定允许这种请求
   3)内联函数省去了普通函数调用时压栈,跳转和返回的开销

1.5.2 默认参数

  C++中可以在函数声明时为参数提供一个默认值,
  当函数调用时没有指定这个参数的值,编译器会自动用默认值代替

  void myPrint(int x = 3)
  {
     printf("x:%d", x);
  }

  函数默认参数的规则
  只有参数列表后面部分的参数才可以提供默认参数值
  一旦在一个函数调用中开始使用默认参数值,那么这个参数后的所有参数都必须使用默认参数值

  //默认参数
  void printAB(int x = 3)
  {
     printf("x:%d\n", x);
  }
  //在默认参数规则 ,如果默认参数出现,那么右边的都必须有默认参数
  void printABC(int a, int b, int x = 3, int y=4, int z = 5)
  {
     printf("x:%d\n", x);
  }
  int main62(int argc, char *argv[])
  {  
     printAB(2);
     printAB();
     system("pause");
     return 0;
  }

1.5.3 函数占位参数

  函数占位参数
  占位参数只有参数类型声明,而没有参数名声明
  一般情况下,在函数体内部无法使用占位参数

  int func(int a, int b, int )
    {
      return a + b;
    }
    int main01()
    {
      //func(1, 2); //可以吗?
      printf("func(1, 2, 3) = %d\n", func(1, 2, 3));
      getchar();
      return 0;
    }

1.5.4 默认参数和占位参数

   可以将占位参数与默认参数结合起来使用
     意义:
       为以后程序的扩展留下线索
       兼容 C 语言程序中可能出现的不规范写法

     C++可以声明占位符参数,占位符参数一般用于程序扩展和对 C 代码的兼容

   int func2(int a, int b, int = 0)
     {
       return a + b;
     }
     void main()
     {
       //如果默认参数和占位参数在一起,都能调用起来
       func2(1, 2);
       func2(1, 2, 3);
       system("pause");
     }
     结论: //如果默认参数和占位参数在一起,都能调用起来

1.5.5 函数重载
 (1) 函数重载概念:
   1 函数重载概念
    函数重载(Function Overload)
    用同一个函数名定义不同的函数
    当函数名和不同的参数搭配时函数的含义不同
   2 函数重载的判断标准
    /*
    函数重载至少满足下面的一个条件:
    参数个数不同
    参数类型不同
    参数顺序不同
    */
   3 函数返回值不是函数重载的判断标准
    实验 1:调用情况分析;实验 2:判断标准

  //两个难点:重载函数和默认函数参数混搭 重载函数和函数指针
    /*
    int func(int x)
    {
      return x;
    }
    int func(int a, int b)
    {
      return a + b;
    }
    int func(const char* s)
    {
      return strlen(s);
    }
    int main()
    {
      int c = 0;
      c = func(1);
      printf("c = %d\n", c);
      c = func(1, 2);
      printf("c = %d\n", c);
      c = func("12345");
      printf("c = %d\n", c);
      printf("Press enter to continue ...");
      getchar();
      return 0;
    }
    */

 (2)函数重载的调用准则
    
  编译器调用重载函数的准则:
    将所有同名函数作为候选者
    尝试寻找可行的候选函数
    精确匹配实参
    通过默认参数能够匹配实参
    通过默认类型转换匹配实参
    匹配失败
    最终寻找到的可行候选函数不唯一,则出现二义性,编译失败。
    无法匹配所有候选者,函数未定义,编译失败。
    
 

  函数重载的注意事项:   
        重载函数在本质上是相互独立的不同函数(静态链编)
        重载函数的函数类型是不同的
        函数返回值不能作为函数重载的依据
        函数重载是由函数名和参数列表决定的。
        函数重载是发生在一个类中里面

  (3)函数重载遇上函数默认参数

        //当函数默认参数遇上函数重载会发生什么
      /*
      int func(int a, int b, int c = 0)
      {
         return a * b * c;
      }
      int func(int a, int b)
      {
         return a + b;
      }
      //1 个参数的允许吗
      int func(int a)
      {
         return a + b;
      }
      int main()
      {
         int c = 0;
         c = func(1, 2); // 存在二义性,调用失败,编译不能通过
         printf("c = %d\n", c);
         printf("Press enter to continue ...");
         getchar();
         return 0;
      }
      */
  (4)函数重载和函数指针结合
    函数重载与函数指针:
        当使用重载函数名对函数指针进行赋值时
        根据重载规则挑选与函数指针参数列表一致的候选者
        严格匹配候选者的函数类型与函数指针的函数类型
      */
      /*
      int func(int x) // int(int a)
      {
          return x;
      }
      int func(int a, int b)
      {
          return a + b;
      }
      int func(const char* s)
      {
          return strlen(s);
      }
      typedef int(*PFUNC)(int a); // int(int a)
      int main()
      {
          int c = 0;
          PFUNC p = func;
          c = p(1);
          printf("c = %d\n", c);
          printf("Press enter to continue ...");
          getchar();
          return 0;
      }
      */

 

    作业:
     1. 设计一个类, 求圆形的周长
       2. 设计一个学生类,属性有姓名和学号,
         可以给姓名和学号赋值
         可以显示学生的姓名和学号

 

 

属于C++编译器对C的扩展

问题: C中可以编译通过吗?
int main()
{
int a = 0;
int &b = a; //int * const b = &a
b = 11; //*b = 11;
return 0;
}
结论:请不要用C的语法考虑 b=11

 

posted @ 2019-10-25 17:24  带你去巴塞罗那  阅读(205)  评论(0编辑  收藏  举报