dufaxing To be a better man

CPP基础与提高Day1

2018-01-01
C++


C++对C 的扩展(Externsion).曾有人戏谑的说,C++作为一种面向对象的语言,名字起的不好,为什么呢?用c 的 语法来看,++ 操作符是post ++ 。

2.1.类型增强:

2.1.1.类型检查更严格

比如,把一个const 类型的指针赋给非const 类型的指针。c 语言中可以通的过,但是 在c++中则编不过去。

int main()
{
    const int a = 100;
    int b = a;
    const int *pa = &a;
    int *pb = pa;
    return 0;
}

2.1.2.布尔类型(bool)

c 语言的逻辑真假用0 和非0 来表示。而c++中有了具体的类型。

int main()
{
    bool flag = true;
    if(flag != false) {
        printf("i know bool type now\n");
    }
    printf("bool size = %d\n",sizeof(bool));
    return 0;
}

2.1.3.真正的枚举(enum)

c 语言中枚举本质就是整型,枚举变量可以用任意整型赋值。而c++中枚举变量,只能 用被枚举出来的元素初始化。

enum season {SPR,SUM,AUT,WIN};
int main()
{
    enum season s = SPR;
    s = 0;//error!!!
    return 0;
}

2.1.4. 表达式的值可被赋值

c 语言中表达式通常不能作为左值的,即不可被赋值,c++中某些表达式是可以赋值的。 比如:

#include <iostream>
using namespace std;
int main(void)
{
    int a,b = 5;
    (a = b) = 10;
    cout<<"a = "<<a<<" b = "<<b<<endl;
    (a<b? a:b) = 200;
    cout<<"a = "<<a<<" b = "<<b<<endl;
    return 0;
}

2.2.输入与输出(cin /cout)

  • 第一个真正意义上的c++程序,c++程序的后缀名为cpp。假设程序名叫xxx 则应该写 成xxx.cpp。

2.2.1.cin && cout

cin 和cout 是C++的标准输入流和输出流。他们在头文件iostream 中定义。

流名 含义 隐含设备 流名 含义 隐含设备
cin 标准输入 键盘 cerr 标准错误 输出屏幕
cout 标准输出 屏幕 clog cerr缓冲 输出屏幕
int main()
{
    char name[30];
    int age;
    cout<<"pls input name and age:"<<endl;
    cin>>name;
    cin>>age;
    // cin>>name>>age;
    cout<<"your name is: "<<name<<endl;
    cout<<"your age is: "<<age<<endl;
    return 0;
}

2.2.2.格式化

2.2.2.1.设置域宽及位数

对于实型,cout 默认输出六位有效数据,setprecision(2) 可以设置有效位数, setprecision(n)«setiosflags(ios::fixed)合用,可以设置小数点右边的位数。

#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
    printf("%c\n%d\n%f\n",'a',100,120.00);
    printf("%5c\n%5d\n%6.2f\n",'a',100,120.00);
    cout<<setw(5)<<'a'<<endl<<setw(5)<<100<<endl
    <<setprecision(2)<<setiosflags(ios::fixed)<<120.00<<endl;
    return 0;
}

2.2.2.2.按进制输出

输出十进制,十六进制,八进制。默认输出十进制的数据。

int i = 123;
cout<<i<<endl;
cout<<dec<<i<<endl;
cout<<hex<<i<<endl;
cout<<oct<<i<<endl;
cout<<setbase(16)<<i<<endl;

2.2.2.3.设置填充符

还可以设置域宽的同时,设置左右对齐及填充字符。

int main()
{
    cout<<setw(10)<<1234<<endl;
    cout<<setw(10)<<setfill('0')<<1234<<endl;
    cout<<setw(10)<<setfill('0')<<setiosflags(ios::left)<<1234<<endl;
    cout<<setw(10)<<setfill('-')<<setiosflags(ios::right)<<1234<<endl;
    return 0;
}

2.3.函数重载(function overload)

2.3.1.引例

如下函数分别求出整理数据和浮点型数据的绝对值:

int iabs(int a)
{
    return a>0? a:-a;
}
double fabs(double a)
{
    return a>0? a:-a;
}

C++ 致力于简化编程,能过函数重名来达到简化编程的目的。

int abs(int a)
{
    return a>0? a:-a;
}
double abs(double a)
{
    return a>0? a:-a;
}

2.3.2.重载规则与调用匹配(overload&match)

  • 重载规则:
    – 1,函数名相同。
    – 2,参数个数不同,参数的类型不同,参数顺序不同,均可构成重载。
    – 3,返回值类型不同则不可以构成重载。
    void func(int a); //ok
    void func(char a); //ok
    void func(char a,int b); //ok
    void func(int a, char b); //ok
    char func(int a); //与第一个函数有冲突,返还值不同。
    

    有的函数虽然有返回值类型,但不与参数表达式运算,而作一条单独的语句。

  • 匹配原则: – 1,严格匹配,找到则调用。
    – 2,通过隐式转换寻求一个匹配,找到则调用。

2.3.3.重载底层实现(name mangling)

  • C++利用name mangling(倾轧)技术,来改名函数名,区分参数不同的同名函数。 实现原理:用v-c- i-f- l- d 表示void char int float long double 及其引用。
void func(char a); // func_c(char a)
void func(char a, int b, double c);
//func_cid(char a, int b, double c)

2.3.4.extern “C”

  • extern “C”的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern “C”后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般之包括函数名。 比如说你用C 开发了一个DLL 库,为了能够让C ++语言也能够调用你的DLL输出(Export)的函数,你需要用extern “C”来强制编译器不要修改你的函数名。

2.4.操作符重载(operator overload)

前面用到的«本身在c 语言中是位操作中的左移运算符。现在又用用流插入运算符,这 种一个字符多种用处的现像叫作重载。在c 语中本身就用重载的现像,比如& 既表示取地址, 又表示位操作中的与。*既表示解引用,又表示乘法运算符。只不过c 语言并没有开放重载机 制。
C++提供了运算符重载机制。可以为自定义数据类型重载运算符。实现构造数据类型也 可以像基本数据类型一样的运算特性。


2.5.默认参数(default parameters)

  • 通常情况下,函数在调用时,形参从实参那里取得值。对于多次调用用一函数同一实参 时,C++给出了更简单的处理办法。给形参以默认值,这样就不用从实参那里取值了。

2.5.1 示例

  • 单个参数
    #include <iostream>
    #include <ctime>
    using namespace std;
    void weatherForcast(char * w="sunny")
    {
      time_t t = time(0);
      char tmp[64];
      strftime( tmp, sizeof(tmp), "%Y/%m/%d %X %A ",localtime(&t) );
      cout<<tmp<< "today is weahter "<<w<<endl;
    }
    int main()
    {
      //sunny windy cloudy foggy rainy
      weatherForcast();
      weatherForcast("rainny");
      weatherForcast();
      return 0;
    }
    
  • 多个参数
#include <iostream>

using namespace std;

float volume(float length, float weight = 4,float high = 5)
{
    return length*weight*high;
}

int main(int argc, char *argv[])
{
    float v = volume(10);//运行结果200
    float v1 = volume(10,20);//运行结果1000
    float v2 = volume(10,20,30);//运行结果6000
    cout<<v<<endl;
    cout<<v1<<endl;
    cout<<v2<<endl;
    return 0;
}

2.5.2.规则

  • 1,默认的顺序,是从右向左,不能跳跃。
  • 2,函数声明和定义一体时,默认认参数在定义(声明)处。声明在前,定义在后,默认参 数在声明处。
  • 3,一个函数,不能既作重载,又作默认参数的函数。当你少写一个参数时,系统无法 确认是重载还是默认参数。
void print(int a)
{

}
void print(int a,int b =10)
{

}
int main()
{
    print(10);//error: call of overloaded 'print(int)' is ambiguous print(10);
    return 0;
}

2.6.引用(Reference)

###2.6.1.引用的概念

  • 变量名,本身是一段内存的引用,即别名(alias)。此处引入的引用,是为己有变量起一个 别名。
    //声明如下:
    int main()
    {
    int a;
    int &b = a;
    }
    

2.6.2.规则

  • 1,引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故而类型 与原类型保持一致,且不分配内存。与被引用的变量有相同的地址。
  • 2,声明的时候必须初始化,一经声明,不可变更。
  • 3,可对引用,再次引用。多次引用的结果,是某一变量具有多个别名。
  • 4,&符号前有数据类型时,是引用。其它皆为取地址。
    int main()
    {
      int a,b;
      int &r = a;
      int &r = b; //错误,不可更改原有的引用关系
      float &rr = b; //错误,引用类型不匹配
      cout<<&a<<&r<<endl; //变量与引用具有相同的地址。
      int &ra = r; //可对引用更次引用,表示a 变量有两个别名,分别是r 和ra
    }
    

    2.6.3.应用

  • C++很少使用独立变量的引用,如果使用某一个变量,就直接使用它的原名,没有必要 使用他的别名。
    //值作函数参数(call by value)
    void swap(int a, int b); //无法实现两数据的交换
    void swap(int *p, int *q); //开辟了两个指针空间实现交换
    
//引用作函数参数(call by reference)
void swap(int &a, int &b)
{
    int tmp;
    tmp = a;
    a = b;
    b = tmp;
}
int main()
{
    int a = 3,b = 5;
    cout<<"a = "<<a<<"b = "<<b<<endl;
    swap(a,b);
    cout<<"a = "<<a<<"b = "<<b<<endl;
    return 0;
}

2.6.4.引用提高

  • 引用的本质是指针,C++对裸露的内存地址(指针)作了一次包装。又取得的指针的优良特 性。所以再对引用取地址,建立引用的指针没有意义。

1,可以定义指针的引用,但不能定义引用的引用。

int a;
int* p = &a;
int*& rp = p; // ok
int& r = a;
int&& rr = r; // error
  • 案例
    #include <iostream>
    using namespace std;
    void swap(char *pa,char *pb)
    {
      char *t;
      t = pa;
      pa = pb;
      pb = t;
    }
    void swap2(char **pa,char **pb)
    {
      char *t;
      t = *pa;
      *pa = *pb;
      *pb = t;
    }
    void swap3(char * &pa,char *&pb)
    {
      char *t;
      t = pa;
      pa = pb;
      pb = t;
    }
    int main()
    {
      char *pa = "china";
      char *pb = "america";
      cout<<"pa "<<pa<<endl;
      cout<<"pb "<<pb<<endl;
      // swap(pa,pb);
      // swap2(&pa,&pb);
      swap3(pa,pb);
      cout<<"pa "<<pa<<endl;
      cout<<"pb "<<pb<<endl;
      return 0;
    }
    
  • 2,可以定义指针的指针(二级指针),但不能定义引用的指针。
    int a;
    int* p = &a;
    int** pp = &p; // ok
    int& r = a;
    int&* pr = &r; // error
    
  • 3,可以定义指针数组,但不能定义引用数组,可以定义数组引用。
    int a, b, c;
    int* parr[] = {&a, &b, &c}; // ok
    int& rarr[] = {a, b, c}; // error
    int arr[] = {1, 2, 3};
    int (&rarr)[3] = arr; // ok µÄ
    
  • 4,常引用
    const 引用有较多使用。它可以防止对象的值被随意修改。因而具有一些特性。
    – (1)const 对象的引用必须是const 的,将普通引用绑定到const 这 个原因比较简单。既然对象是const 的,表示不能被修改,引用当然也不能修改,必须使 用const 引用。实际上,const int a=1; int &b=a;这种写法是不合法的,编译不过。
    – (2)const 引用可使用相关类型的对象(常量,非同类型的变量或表达式)初始化。这个是 const 引用与普通引用最大的区别。const int &a=2;是合法的。double x=3.14; const int &b=a;也是合法的。
    const 引用的目的是,禁止通过修改引用值来改变被引用的对象。const 引用的初始化 特性较为微妙,可通过如下代码说明:
    double val = 3.14;
    const int &ref = val; // int const & int & const ??
    double & ref2 = val;
    cout<<ref<<" "<<ref2<<endl;
    val = 4.14;
    cout<<ref<<" "<<ref2<<endl;
    

    上述输出结果为3 3.14 和3 4.14。因为ref 是const 的,在初始化的过程中已经给 定值,不允许修改。而被引用的对象是val,是非const 的,所以val 的修改并未影响ref 的值,而ref2 的值发生了相应的改变。
    那么,为什么非const 的引用不能使用相关类型初始化呢?实际上,const 引用使用相 关类型对象初始化时发生了如下过程:

    int temp = val;
    const int &ref = temp;
    

    如果ref 不是const 的,那么改变ref 值,修改的是temp,而不是val。期望对ref 的 赋值会修改val 的程序员会发现val 实际并未修改。

    int i=5;
    const int & ref = i+5;
    //此时产生了与表达式等值的无名的临时变量,
    //此时的引用是对无名的临时变量的引用。故不能更改。
    cout<<ref<<endl;
    
  • 5,尽可能使用const
    **use const whatever possible **原因如下:
    – 1,使用const 可以避免无意修改数据的编程错误。
    – 2,使用const 可以处理const 和非const 实参。否则将只能接受非const 数据。
    – 3,使用const 引用,可使函数能够正确的生成并使用临时变量(如果实参与引用参 数不匹配,就会生成临时变量)

2.7.new/delete Operator

c 语言中提供了malloc 和free 两个系统函数,完成对堆内存的申请和释放。而c++则 提供了两关键字new 和delete ;

2.7.1.new/new[]用法:

  • 1.开辟单变量地址空间
    int *p = new int; //开辟大小为sizeof(int)空间
    int *a = new int(5); //开辟大小为sizeof(int)空间,并初始化为5
    
  • 2.开辟数组空间
    一维: int *a = new int[100]{0};开辟一个大小为100 的整型数组空间
    int **p = new int*[5]{NULL}
    二维: int (*a)[6] = new int[5][6]
    三维: int (*a)[5][6] = new int[3][5][6]
    四维维及其以上:依此类推.
    

    2.7.2.delete /delete[]用法:

int *a = new int;
delete a; //释放单个int 的空间
int *a = new int[5];
delete []a; //释放int 数组空间

2.7.3.注意事项

  • 1,new/delete 是关键字,效率高于malloc 和free.
  • 2,配对使用,避免内存泄漏和多重释放。
  • 3,避免,交叉使用。比如malloc 申请的空间去delete,new 出的空间被free;

Comments

Content