引用

类型标识符 &引用名=目标变量名;
如:int a;
int &ra=a; //定义引用ra,它是变量a的引用,即别名
由于指针变量也是变量,所以,可以声明一个指针变量的引用。
方法是: 类型标识符 *&引用名=指针变量名;

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream.h>
void main()
{
int *a; //定义指针变量a
int *&p=a; //定义引用p,初始化为指针变量a,所以p是a的引用(别名)
int b=10;
p=&b; //等价于a=&b,即将变量b的地址赋给a。
cout<<*a<<endl; //输出变量b的值
cout<<*p<<endl; //等价于cout<<*a;
cout<<a<<endl; //输出a和p的地址
cout<<p<<endl;
}
  • 不能声明引用的引用,也不能定义引用的指针

  • 不能建立数组的引用,因为数组是一个由若干个元素所组成的集合,所以就无法建立一个数组的别名。

  • 不能建立空指针的引用,如:不能建立int &rp=NULL;

  • 不能建立空类型void的引用,如:不能建立void &ra=3;因为尽管在C++语言中有void数据类型,但没有任何一个变量或常量属于void类型。

  • 函数可以返回一个引用,将函数说明为返回一个引用的主要目的是:为了将函数用在赋值运算符的左边。
    要以引用返回函数值,则函数定义时要按以下格式:

    1
    2
    类型标识符 &函数名(形参列表及类型说明)
    {函数体}
  • 用const限定引用
    声明方式:const 类型标识符 &引用名=目标变量名;
    用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性。

类和对象

  • private处于类体中第一部分时,关键字private可以省略。

  • 数据成员可以是任何数据类型,但不能用自动(auto)、寄存器(register)或外部(extern)进行声明。

  • 不能在类声明中给数据成员赋值。C++规定,只有在类对象定义之后才能给数据成员赋初值

  • 在声明类的同时定义的对象是一种全局对象,在它的生存期内任何函数都可以使用它。

  • 在类的内部所有成员之间都可以通过成员函数直接访问,但是类的外部不能访问对象的私有成员。

    例如下面是一个存在错误的程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    #include <iostream.h>
    class Date {
    public:
    void setDate(int y,int m,int d);
    void showDate();
    private:
    int year;
    int month;
    int day;
    };
    void Date∷setDate(int y,int m,int d)
    {
    year=y;
    month=m;
    day=d;
    }
    inline void Date∷showDate()
    {
    cout<<year<<"."<<month<<"."<<day<<endl;
    }
    void main()
    {
    Date date1,date2;
    cout<<"Date1 set and output:"<<endl;
    date1.setDate(1998,4,28);
    cout<<date1.year<<"."<<date1.month<<"."<<date1.day<<endl; //错误
    cout<<"Date2 set and output:"<<endl;
    date2.setDate(2002,11,14);
    cout<<date2.year<<"."<<date2.month<<"."<<date2.day<<endl; //错误
    }

    应该改为:

    1
    2
    date1.showDate(); 
    date2.showDate();
  • 说明为私有的成员只能被类中成员函数访问,不能在类的外部,通过类的对象进行访问。

  • 对象赋值语句:两个同类型的变量之间可以相互赋值。同类型的对象间也可以进行赋值,当一个对象赋值给另一个对象时,所有的数据成员都会逐位拷贝。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include<iostream.h>
    class abc{
    ......
    };
    main()
    { abc o1,o2;
    o1.init(12,34);
    o2=o1; // 将对象o1数据成员的值赋给对象o2
    ......
    }

    上面的对象赋值是通过缺省的赋值运算符函数实现的。当类中存在指针时,使用缺省的赋值运算符进行对象赋值,可能会产生错误。

  • 类的构造函数是在对象进入其作用域时(对象使用前)调用的。

  • 构造函数创建对象的两种语法

    1
    2
    类名  对象名[(实参表)];
    类名 *指针变量 = new 类名[(实参表)];
  • 成员初始化表的构造函数

    对于常量类型和引用类型的数据成员,不能在构造函数中用赋值语句直接赋值,C++提供初始化表进行置初值。

    • 带有成员初始化表的构造函数的一般形式如下:

      1
      2
      3
      4
      5
      6
      类名::构造函数名([参数表])[:(成员初始化表)]
      {
      // 构造函数体
      }
      成员初始化表的一般形式为:
      数据成员名1(初始值1),数据成员名2(初始值2),……
    • 如果需要将数据成员存放在堆中或数组中,则应在构造函数中使用赋值语句,即使构造函数有成员初始化表也应如此。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      class  C{
      public:
      C(int I,char Ch,float F,char N[]):i(I),ch(Ch),f(F)//学习初始化列表写法
      { strcpy (name,N);}
      private:
      int i;
      char ch;
      float f;
      char name[25];
      };
  • *类成员是按照它们在类里被声明的顺序初始化的,与它们在初始化表中列出的顺序无关。*

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include<iostream.h>
    class D {
    public:
    D(int i):mem2(i),mem1(mem2+1)
    {
    cout<<"mem1: "<<mem1<<endl;
    cout<<"mem2: "<<mem2<<endl;
    }
    private:
    int mem1;
    int mem2;
    };
    void main()
    {
    D d(15);
    }
    //运行结果
    //mem1: -858993459
    //mem2: 15
  • 拷贝构造函数

    • 调用拷贝构造函数的三种情况

      • 当用类的一个对象去初始化该类的另一个对象时。

        1
        2
        3
        Coord p2(p1); // 用对象p1初始化对象p2,拷贝构造函数被调用(代入法)
        Coord p3=p1; // 用对象p1初始化对象p3,拷贝构造函数被调用(赋值法)
        //这时需要调用拷贝构造函数
* 当函数的形参是类的对象,调用函数,进行形参和实参结合时。  

  
1
2
3
4
5
6
7
8
9
10
11
 fun1(Coord p)      // 函数的形参是类的对象
{
p.print();
}
main()
{
Coord p1(10,20);
fun1(p1); // 当调用函数,进行形参和实参结合时,
调用拷贝构造函数
return 0;
}
* 当函数的返回值是对象,函数执行完成,返回调用者时。
1
2
3
4
5
6
7
8
Coord fun2()
{ Coord p1(10,30);
return p1; } // 函数的返回值是对象

main()
{ Coord p2;
P2=fun2(); // 函数执行完成,返回调用者时,调用拷贝构造函数
return 0; }
  • 析构函数

    • 析构函数不能重载,一个类中只有一个
    • auto局部对象:局部自动对象(例如在函数中定义的对象),则在建立对象时调用其构造函数。如果函数被多次调用,则在每次建立对象时都要调用构造函数。在函数调用结束、对象释放时先调用析构函数。
    • static局部对象:如果在函数中定义静态局部对象,则只在程序第一次调用此函数建立对象时调用构造函数一次,在调用结束时对象并不释放,因此也不调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用析构函数.