面向对象的基本任务是描述对象并对对象进行归类总结。类类型与int类型一样,也没有任何内存分配。类的属性和对外接口是类定义的重点和难点,原则是尽量让内部操作私有化,提供简单易用的接口函数。
1、类的相关问题
在定义类的数据成员时,不能像定义变量一样进行初始化,若在定义时未指明访问限定符,默认为private;
在定义类的方法时,若方法中不用修改类的数据成员,则最好在方法声明的最后使用const关键字,表示用户不能在此方法中修改类的数据成员。若类中包含指针成员,在const方法中不可以重新为指针赋值,但可以修改指针所指向的地址中的数据。
类中,除静态成员可以直接访问外,其它成员是通过对象来实现访问的。在定义类时并没有分配存储空间,只有当实例化对象时,才分配存储空间。也可以将类对象声明为一个指针,并使用 ?new运算符为其分配内存,如下所示:
若将类对象声明为常量指针,则只能调用类中的常量方法。
2、构造函数与析构函数
每个类都有构造函数与析构函数,构造函数在定义对象时被调用,析构函数在对象释放时被调用。构造函数负责类对象生成之前的初始化,析构函数负责对象销毁后的处理。
构造函数没有返回值,若用户没有提供构造函数和析构函数,则使用默认的构造析构函数。一个类可以包含多个构造函数,各函数通过重载来进行区分,下面是指针调用参数构造函数进行初始化:
类体中定义的数据成员不能初始化,故类中初始化只能借助构造函数的初始化列表实现。类成员函数,若在类体中定义,函数前即使没有使用inline,该成员函数也被认为是内联函数。
析构函数没有返回值,也没有参数,故不能重载。
3、复制构造函数
复制构造函数与类的其它函数构造类似,以类名作为函数的名称,第一个参数为该类的常量引用类型。
下面的三种情况要用到复制构造函数:
对象以值传递的方式传入函数参数;
对象以值传递的方式从函数返回;
对象需要通过另外一个对象进行初始化;
编译器有默认的拷贝构造函数,这个函数仅仅是使用老对象的数据成员的值对“新对象”数据成员一一进行赋值,这称为浅拷贝,即只是对象中数据成员进行简单的赋值,这种方式在对象中存在动态成员时则会出现问题,比如对象中的指针。浅拷贝只是使两个指针有相同的值,而不是我们需要的两块不同的地址。
深拷贝,对于对象中动态成员,不仅仅赋值,还重新动态分配空间,从而使得指针指向两块不同的内存,但内存中的值相同。
在编写函数时,尽量按引用方式传递参数,这样可以避免调用复制构造函数,可以极大地提高程序效率。也可以将拷贝函数放在private中,从而防止默认拷贝的发生。
4、静态类成员
普通类成员只能通过实例化对象访问,静态类成员还可以通过类名直接访问,访问时用::域访问符。在定义静态数据成员时,要在类体外部对静态数据成员初始化。静态数据成员是被所有类对象共享的。
静态数据成员可以是当前的类型,而其他数据成员只能是当前类的指针或引用类型,如:
针对静态数据成员有如下几点:
静态数据成员可以作为成员函数的默认参数,但是普通成员不可以;
类的静态成员函数只能访问类的静态成员,不能访问普通数据成员;
静态成员函数末尾不能用const关键字修饰;
静态数据成员和静态成员函数在类体之外初始化或定义时,去掉static关键字;
5、运算符重载
运算符重载要用到operator关键字,它其实是函数重载的一种,因为运算符本来就是一个函数。对于重载的运算符,两个函数不能交换顺序,重载是什么顺序,只能用这各顺序调用。
对于++和–运算符,由于涉及前置和后置,故默认情况下,重载运算符没有参数,表示是前置运算;若用整型int作为参数,则表示后置运算。
并不是所有的运算符都可以重载,大多数是可以重载的,但是::,?,:,. 是不能重载的。
6、有关类的sizeof问题
空类也会被实例化,编译器会给类隐含添加一个字节,故空类的sizeof()结果为1;sizeof()用来计算字符串的长度时包含”