C++面向对象程序设计——类和对象的进一步讨论

    xiaoxiao2021-03-25  87

    构造函数:

    构造函数是一种特殊的成员函数,它不需要用户来调用它,而是在建立对象时自动执行。

    在一个类中定义了全部是默认参数的构造函数后,则不再定义重载构造函数(否则容易出错)。

     

    初始化表:

         example: Box::Box(int h, int w ,intlen):height(h),width(w),length(len) { }

    析构函数:

    析构函数的作用是在撤销对象占用的内存之前完成一些清理工作。

    当对象的生命周期结束时,会自动执行析构函数。

    析构函数没有返回值,没有函数类型,也没有函数参数,由于没有函数参数,因此也不能被重载。(一个类可以有多个构造函数,但是只能有一个析构函数。)

    析构函数可以用来执行用户希望在最后一次使用对象之后所执行的任何操作。

     

    调用构造函数和析构函数的顺序:

    一般情况下(对同一类存储类别的对象而言)先构造的后析构,后构造的先析构,它向当于一个栈,先进后出。

     

    对象数组:

    在建立数组时,如果对象数组有10个元素,则需要调用10次构造函数。

    如果构造函数只有一个参数,在定义数组时可以直接在等号后面的花括号内提供实参。

    example: Studentstu[3]={10,20,30};

    当构造函数有多个参数时,可以使用一下方法定义对象数组时对对象初始化。

    example:

    Student stu[3]=

    {

        Student(100,10,88),Student(101,11,84),Student(103,13,80)

    }

     

    对象指针:对象空间的起始地址就是对象的指针。

    定义对象指针的一般形式:

    类名 *对象指针名;

     

    指向对象成员的指针:

    eg: int *p;

    p = &t.hour;

    cout << * p << endl;

     

    指向对象成员函数的指针:

    定义指向公用成员函数的指针变量的一般形式:

    数据类型名 (类名 :: *指针变量名)(参数列表);

    使指针变量指向一个公用成员函数的一般形式为:

    指针变量名 = &类名::成员函数名;

    example :

         void (Time::*p2)( ); //定义p2为指向Time类中公有成员函数的指针变量

         p2 = &Time::get_time;//函数指针指向公有成员函数的入口地址

     

    this指针:

    在每一个成员函数(静态成员函数除外)中都包含一个特殊的指针,这个指针的名字是固定的,称为this。它是指向本类对象的指针,他的值是当前被调用的成员函数所在对象的起始地址。                

     

    常对象:

    在定义对象时指定对象为常对象。常对象必须要有初值,如:Time const t(12,34,46); //t是常对象

    这样,在所有场合中,对象t中的所有成员的值都不能被修改。

    如果定义了一个常对象,只能调用其中的const成员函数,而不能调用非const成员函数。

    定义常对象的一般形式:

    类名 const 对象名[(实参列表)];

    const 类名 对象名[(实参列表)];

    如果一个对象被声明为常对象,则不能调用该对象的非const型的成员函数(除了由系统自动调用的隐式的构造函数和析构函数)这是为了防止这些函数会修改常对象中数据成员的值。                   

    常成员函数可以访问常对象中的数据成员,但仍然不允许修改常对象中数据成员的值。                                                                  

    mutable可变数据成员,当成员数据声明为mutable时,可以用声明为const的成员函数来修改它的值。

     

    常数据成员:

    只能通过构造函数的参数初始化表对常数据成员进行初始化。不能采用构造函数中对常数据成员赋初值的方法。如以下用法是非法的:

    Time::Time(int h)

    {

         hour = h; //假设hour为常数据成员则此用法非法

    }

     

    常成员函数:

    常成员函数只能引用本类中的数据成员,而不能修改他们。

    定义常成员函数的一般形式:

    函数类型  函数名()  const; //const的位置在函数名和括号之后

    const是函数类型的一部分,在声明和定义函数时都要有const关键字,在调用时不必加const。

    常成员函数可以引用const数据成员,也可以引用非const的数据成员,

    const数据成员可以被const成员函数引用也可以被非const的成员函数引用。

     

    指向对象的常指针:

    将指针变量声明为const型这样指针值始终保持值为初值,不能改变。如:

    Time t1(1,2,3),t2;

    Time *const ptr;

    ptr1 = &t1;            //正确

    ptr1 = &t2;         //错误,ptr不能改变指向

     

    定义指向常对象的常指针一般形式:

         类名 * const 指针变量名;

     

    指向常对象的指针变量:

    定义指向常变量的一般形式为:

         const 类型名 * 指针变量名;

    如果一个变量已被声明为常变量,只能用指向常变量的指针指向它,而不能用一般的(指向非const型变量的)指针变量去指向它。

    如果定义了一个指向常对象的指针变量,并使它指向一个非const的对象,则其指向的对象是不能通过指针来改变的(但指针变量本身的值是可以改变的)。

     

    对象的赋值和复制:

    对象的赋值:

    同类的对象之间可以相互赋值,这里指的是对象中所有的数据成员的值。一般形式 : 对象名1 = 对象名2;

    对象的赋值只是对其中的数据成员赋值,而不对成员函数赋值,类的数据成员中不能包括动态分配的数据,否则在赋值时可能出现严重的后果。

     

    对象的复制:

    对象的复制就是调用复制构造函数,如果用户未定义复制构造函数,则编译系统会自动提供一个默认的复制构造函数,其作用只是简单的复制类中每个数据成员。其一般形式为:

    类名 对象2(对象1);

    类名 对象名1 = 对象名2;

     

    对象的赋值和复制的不同:

    对象的赋值是对一个已经存在的对象的赋值,因此必须先定义被赋值的对象,才能进行赋值。

    而对象的复制则是从无到有的建立一个新对象,并使它与一个已有的对象完全相同(包括对象的结构和成员的值)。

     

    哪些情况下需要调用复制构造函数:

    1、程序中需要建立一个新对象,并用一个同类对象去初始化它。

    2、当函数的参数为类的对象时。在调用函数时需要将实参对象完整地传递给形参,也就需要建立一个实参的拷贝。

    3、函数的返回值是类的对象。在函数调用完毕将返回值带回函数调用处时。此时需要将函数中的对象复制给一个临时对象并传给该函数的调用处。

    浅拷贝和深拷贝:

        浅拷贝:指的是把对象的值完全赋值给另外一个对象,如a=b。

                但是当对象b中有一个指针成员变量指针已经申请了内存,

                那么a对象中的那个指针成员变量也将指向同一块内存。

                *(也就是并不会申请新的空间去完整复制b对象指针对象所指向的内容)

        深拷贝:如果一个类拥有指针成员变量并申请了内存,当这个类的对象发生复制的过程时,

                资源重新分配,这就叫做深拷贝。

                *(也就是会申请新的空间去完整复制对象指针对象所指向的内容)

     

    静态成员:

    静态成员变量:

    静态数据成员用于同类的多个对象之间实现数据共享。它以关键字static开头。

    静态数据成员为各个对象所共有,而不只属于某个对象的成员,所有对象都可以引用它,它只在内存中占用一份空间。

    静态数据成员不属于某一个对象,在为对象所分配的空间中不包括静态数据成员所占的空间。静态数据成员是在所有对象之外单独开辟空间。

    只要类中定义了静态数据成员,即使不定义对象,也为静态数据成员分配空间,它可以被引用。

    静态数据成员是在程序编译时被分配空间的,到程序结束时才施放空间。

    静态数据成员可以初始化,但是只能在类体外进行初始化。如: intBox::height = 10;其一般形式:数据类型 类名::静态数据成员名 = 初值; 不必在初始化语句中加static。

    不能用参数初始化表对静态数据成员初始化。如果未对静态数据成员赋初值,则编译系统会自动赋予初值0

    静态数据成员既可以通过对象名引用,也可以通过类名来引用。如:类名::静态数据成员名;/对象名.静态数据成员名;

    如果静态成员被定义为私有的,则不能在类外直接引用,而必须通过公用的成员函数引用。

    静态数据成员的作用域只限于定义该类的作用域内。

     

    静态成员函数:

    静态成员函数是类的一部分,而不是对象的一部分。如果要在类外调用公用的静态成员函数,要用类名和域名运算符“::”。

    静态成员函数不属于某一对象,它与任何对象都无关,因此静态成员函数没有this指针。因此它无法对一个对象中的非静态成员进行默认访问(即在引用数据成员时不指定对象名)。

    静态成员函数可以直接引用本类中的静态数据成员。

    静态成员函数主要用来访问静态数据成员,而不访问非静态成员,如果一定要引用本类的非静态成员,应该加对象名和成员运算符“.”。

     

    友元:

    友元可以访问与其好友关系的类中的私有成员,友元包括友元函数和友元类。

     

    友元函数:

    如果在本类以外的其他地方定义了一个函数(这个函数可以是不属于任何类的成员函数,也可以是其他类的成员函数),在类体中用friend对其进行声明,此函数就成为本类的友元函数。

     

    友元成员函数:

    友元函数不仅可以是一般函数(非成员函数),而且可以是另一个类中的成员函数。

    类的提前声明:class 类名;

     

    友元类:

    不仅可以将一个函数声明为一个类的朋友,而且可以把一个类声明为另一个类的朋友。

    友元类可以访问另一个类中所有的成员。

    声明友元类的一般形式:friend 类名;

     

    友元要点:

    1、友元的关系是单向的而不是双向的。

    2、友元的关系是不能传递的。

     

    类模板:

    类模板用于解决 多个类中函数的功能相同而数据类型不同却需要重复定义类的情况,使用类模板可以用声明好的类型名去替换类中的数据类型,在使用类模板定义对象时只要直接指定为确定的数据类型即可。由于类模板包含类型参数,因此又称为参数化的类。类模板是类的抽象,类是类模板的实例。

    声明类模板:

    template <class 类型参数名1,class 类型参数名2,...>     //声明一个类模板,虚拟类型名就是(类型参数名,用于替换不确定的实际数据类型)

    使用类模板定义对象:

    类模板名 <实际类型名1,实际类型名2,...> 对象名(参数表);    //使用类模板定义对象

    在类模板外定义成员函数:

    template <class 类型参数名1,class 类型参数名2,...>

    函数类型 类模板名<虚拟类型参数1,虚拟类型参数2,...>::成员函数名(函数形参表){...}

     

    注意:

    类模板参数可以有一个或者多个,每个类型前面都必须加class  如:template <class T1,class T2>

    类模板的作用域限定在定义类模板的文件内,在其他文件中不可以使用。

    类模板可以作为一个基类,派生出派生模板类。

     

    eg:

    template <classnumtype>     //声明类模板

    class Compare

    {

        public:

    Compare(numtypea,numtype b)

    {

         x=a;y=b;

    }

    numtype max()

         {return(x>y)? x : y;}

    numtype min()

    {return (x<y)?x: y;}

     

    private:

         numtypex,y;

    }

     

    //在类模板外定义成员函数

    template <classnumtype>

    numtypeCompare<numtype>:: min()

    {return (x<y)? x:y;}

     

    Compare <int>cmp(1,2);     //定义对象

     

     

     

    转载请注明原文地址: https://ju.6miu.com/read-38226.html

    最新回复(0)