代码改变世界

C++ - C++我不知道的基础(二)

2015-08-26 11:03  HermitCarb  阅读(303)  评论(0编辑  收藏  举报

第四章、函数
25.C++函数的返回值类型不可以是数组,如果确需返回数组类型,可以反回一个指针类型,同时记得通过引用额外的类型参数纪录数组长度。

 

26.函数默认值参数:如
    int OutputNum(int number = 5)
    {
        cout<<number<<endl;
    }
则可以选择在调用函数时使用不使用参数,应注意在多个相同类型并且提供默认值的函数在调用时,提供的部分参数优先匹配到前面的类型相同的形参上。

 

27.const参数:不希望在函数内改变参数值的情况先可以使用const标示,称为const参数。

 

28.指针/引用参数和值/引用传递:值传递把参数拷贝一份传递给函数,引用传递把参数的地址传递给函数。如果参数的类型是指针类型、引用类型、数组类型、对象,则函数是引用传递,其他情况是值传递。使用引用传递时要注意引用类型的特点:形参是目标的别名,无法替换目标,不能引用字面常量和临时值。使用指针传递时要注意指针的合法性(不能是空指针)及特点:不加const限定时可以随意修改指针指向的位置。

 

29.数组参数。数组作为参数时被视为(作为)指向数组第一个元素的指针进行传递。注意数组传递时通过引用额外的类型参数纪录数组长度或使用第12条笔记的第三种指针来限定数组的长度。

 

30.内联函数:内联函数是指用inline关键字修饰的函数。在类内定义的函数被默认成内联函数。内联函数从源代码层看,有函数的结构,而在编译后,却不具备函数的性质。编译时,编译器将函数代码复制到函数引用处,提高了执行效率但增加了代码长度。

 

31.重载函数:唯一区分重载函数的是参数个数和参数类型。重载函数发生作用域覆盖时,作用域小的重载函数在其作用域中会隐藏作用域大的(所有)重载函数,称为隐藏重载函数。

 

32.递归(直接递归和间接递归)可以简化部分程序设计的难度,但会增加系统的开销。

 

33.函数指针:C++中,函数名实际上是指向函数的常指针,如:
    int OutputNum(int number){ ...... };    //定义一个返回类型为空、有一个整型参数的函数
    void (*funOutputNum)(int number);       //定义了一个可以指向“返回类型为空、有一个整型参数的函数”的指针。
    funOutputNum = &OutputNum;             //对函数指针赋值,也可以写为:funOutputNum = OutputNum;
    (*funOutputNum)(5);                     //通过函数指针调用其指向的函数,也可以写为:funOutputNum(5);

 

34.函数模版(关键字:template):示例如下:
    template <class MyType>
    MyType Add(MyType x, MyType y)
    {
    return x + y;
    }
上面定义了一个函数模版,它可以实现基本数据类型的加法运算,如果你在某个类中重载了加法运算符,那么,上面的函数模版也可以实现该类对象的加法。“template”表示定义一个模版;“< >”表示模版参数(有两种:一种是模版类型参数,以class或typedef标示,后跟用户定义的合法的标示符;另一种是模版非类型参数,通常是一个常数,如数组长度);最后面是一个函数。函数模版也可以进行重载(重载函数模版)。

 

35.局部变量在定义时如果没有提供初始值,系统不会提默认的初始值,此时使用会出现错误:局部变量尚未初始化。全局变量在定义时如果没有提供初始值,系统会提默认的初始值0。

 

36.命名空间:解决对象重名的一种方法。引用命名空间中的对象,需要在对象前使用命名空间作为前缀(使用域限定符:“::”)。使用命名空间时要注意对象的隐藏问题。当使用using引用的多个命名空间的中的对象存在重名则必须使用命名空间前缀。命名空间可以嵌套,但使用命名空间中的元素时,必须给出该元素的完整引用路径。命名空间可以定义于多个文件中,命名空间的大小为这些相同的命名空间的总和。一般情况下,是在头文件中声明命名空间中的函数,在源文件中定义命名空间中的函数,将程序的声明和实现分开。可以定义未命名的命名空间,其中的对象只适用于其作用域。

 


第五章、类和对象
37.类定义时可以只声明其函数成员(提供方法原型),然后在类外实现时须在函数名前使用类名和域限定符“::”作为方法前缀,标示方法属于的类。

 

38.定义类数据成员时不能直接对其初始化。可以在构造函数函数体前(方法原型后)使用冒号“:”运算符进行初始化。如:
    CUser():userage = 10 { ...... }

 

39.C++中声明了类的对象(定义对象)即可访问其成员,即声明时会附带初始化。如:
    CUser user;
    user.getUsername();
此时可以用“.”运算符访问其成员。定义类对象时也可以将类对象声明为一个指针,程序中可以使用new运算符为指针分配内存。如:
    CUser *user = new CUser();
但此时要用“->”运算符来访问其成员。

 

40.C++类的访问限定符默认为:private。常用getter和setter方法进行封装。getter方法一般不需要修改类的数据成员,可已在函数体前(方法原型后)使用const关键字。如:
    char *getPassword()const{ ...... }
如果非要在const成员内修改某一个数据成员,可以在定义数据成员时使用mutable关键字。

 

41.构造函数:与类名相同的函数称为构造函数,其中没有参数的称为默认构造函数。构造函数可以重载,可以提供默认值,但应注意提供默认值后会不会和其他重构函数冲突。若程序员提供了构造函数则系统不再提供默认的构造函数。

 

42.复制构造函数:当函数或方法采用按值传递时,编译器会调用该类的复制构造函数来复制实际参数到被调用的函数。其函数名与类名相同,有一个该类的常量引用实例(没必要修改参数,只是复制)。如:
    Cuser(const CUser &user){ ...... }

 

43.析构函数:在对象释放时调用,用于进行对象的清理工作,如果对象是在堆栈中构建的,则对象在其作用域消失时释放。析构函数名与类名相同,函数名前须加“~”前缀,且没有参数和返回值,所以其无法被重载,一个类只有一个析构函数。

 

44.继承关系:

继承关系
  public protected private 成员访问限定符
public public protected    
protected protected private    
private private private    
继承限定符        

 

45.虚函数和动态绑定:虚函数是实现多态的重要方式。实现父类对象使用子类的实例化方法后调用子类中和父类同名的方法,即编译器动态绑定,默认是静态绑定。可以在父类方法前加上“virtual”关键字定义为虚方法,此时子类重新定义该方法(函数名和参数列表相同)默认也为虚方法。

 

46.纯虚方法和抽象类:包含纯虚方法的类称为抽象类,纯虚函数如下:
    virtual void OutputNum() = 0;
抽象类不能够实例化,其派生的子类如果要实例化(不作为抽象类)就要实现其父类(祖先类中没有实现)的抽象方法。换句话说,如果一个类没有实现其父类(祖先类中没有实现)的所有纯虚方法,那么这个类也是抽象类。

 

47.对象类型转换:分为向上转换(装箱,可以默认转换)和向下转换(拆箱:必须强制转换,不建议C中的强制类型转换,建议C++中的动态转换)。

 

48.虚继承:使得子类只有一个基类。实现上在上层继承时使用“virtual”关键字,下层可以不使用。

 

49.构造函数自动转换:如:
    class CUser{
        ......
        CUser(int num){ ...... }
        ......
    }
    ......
    CUser user = 5; //不会报错,调用上面的构造函数
构造函数自动转换容易让人产生误解,为防止自动转换可以在该构造函数前使用“explicit”关键字进行标示。
    
50.运算符重载:如“+”重载时:
    CUser operator + (CUser &refUser){ ...... }   //一个参数指运算上参数,“(user) + refUser”
    CUser operator + (int num){ ...... }   //一个参数指运算上参数,“(user) + num”
    CUser operator + (int num, CUser &refUser){ ...... } //两个参数指运算双方,有序,“num + (user)”
对于“++”和“--”:
    void operator ++ (){ ...... }    //不带参数默认为前置运算
    void operator ++ (int){ ...... }    //带整数参数为后置运算

 

51.运算符重载分局部运算符重载(包括类中运算符重载)和全局运算符重载。其中运算符“=”、“[]”、“->”和“()”只能使用类成员函数进行重载。

 

52.要实现对象赋值给整数,可以使用C++提供的转换运算符,如:
    class CUser{
        ......
        operator int(){ ...... }  //定义整型的转换运算符
        ......
    }
    ......
    CUser user;
    int num = user;    //调用上面的转换运算符

 

53.运算符重载要点:
    a.大多数运算符可以重载,但“::”、“?”、“:”、“.”不能被重载。
    b.不能构建新的运算符。
    c.不能改变原有运算符操作数的个数。
    d.不能改变原有运算符的优先级。
    e.不能改变原有运算符的结合性。
    f.不能改变原有运算符的语法结构。
    g.一元操作数可以是不带参数的成员函数,或者是带一个参数的非成员函数。
    h.二元操作数可以是带一个参数的成员函数,或者是带两个参数的非成员函数。
    i.“=”、“[]”、“->”和“()”运算符重载只能定义为成员函数。
    j.“->”运算符重载的返回值必须是指针类型或者能够使用“->”运算符类型的对象。
    k.重载“++”和“--”运算符时,带一个int参数表示后置运算,不带参数表示前置运算。

 

54.友元函数和友元类:在一个函数里访问其他类的私有成员,可把该函数定义为这个类的友元函数。同理,在一个类的多个方法里访问其他类的私有成员,可把该类定义为另一个类的友元函数。即,你说我是你的朋友,我就可以问你一些比较私人的问题。