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;    // 错误
    }

privateconst 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;
      }
           因为重载函数的参数表不同,而调用函数的语句给出的实参必须和参数表中的形参个数和类型都匹配,因此编译器才能够判断出到底应该调用哪个函数。
posted @ 2019-04-29 21:31  飘柔的小卷毛  阅读(236)  评论(0编辑  收藏  举报