《C++面向对象程序设计》 谭浩强 第三章 总结

1.构造函数:

(1).构造函数的名字与类名同名;它不具有任何类型,不返回任何值;构造函数允许重载;构造函数自动调用(在类对象进入其作用域时就会调用构造函数),不需要用户去调用。

(2).带参数的构造函数:构造函数首部的一般格式为:构造函数名(类型1 形参1,类型2 形参2,...)

   定义对象的一般格式为:类名 对象名(实参1,实参2,....)

(3).用参数初始化表对数据成员初始化

参数初始化表又称“冒号语法”或“内构的函数”

该种方法的一般形式为:Box :: Box(int h,int w,int len):height(h),width(w),length(len){}

(4).构造函数的重载

概念:构造函数具有相同的名字,而参数的个数或参数的类型不相同。

(5).默认构造函数:在调用构造函数时不必给出实参的构造函数,也称缺省构造函数,无参的构造函数属于默认构造函数。

一个类只能有一个默认构造函数,否则,系统就无法辨别应执行哪个构造函数。

示例:

#include <iostream>

using namespace std;

class Box
{
public:
    Box();
    Box(int h,int w,int len):height(h),width(w),length(len) {}
    //带参数的构造函数,这里用参数初始化表声明定义该构造函数
    
    int volume();//在类内声明函数,在类外定义函数
private:
    int height;
    int width;
    int length;
};

Box::Box()//不带参数的构造函数
{
height = 10;
width = 10;
length = 10;
}

int Box :: volume()//类外定义函数,要在函数名之前添加该函数属于的类类型,并运用作用域限定符
{
return (height * width * length);
}

int main()
{
Box box1;
cout << "the volume of box1 is" << box1.volume() << endl;
Box box2(15,30,29);//指定三个实参
cout << "the volume of box2 is" << box2.volume() << endl;
return 0;
}

在使用默认的构造函数时,在上面的示例函数稍作更改,

class Box
{
public:
    
    Box(int h = 10,int w = 10,int len = 10);//或Box(int = 10,int = 10,int = 10);
    
    int volume();//在类内声明函数,在类外定义函数
private:
    int height;
    int width;
    int length;
};

Box::Box(int h,int w,int len)//不带参数的构造函数
{
height = h;
width = w;
length = len;
}

int Box :: volume()//类外定义函数,要在函数名之前添加该函数属于的类类型,并运用作用域限定符
{
return (height * width * length);
}

int main()
{
Box box1;
cout << "the volume of box1 is" << box1.volume() << endl;
Box box2(15);//指定1个实参
cout << "the volume of box2 is" << box2.volume() << endl;
Box box3(15,30);//指定2个实参
cout << "the volume of box2 is" << box3.volume() << endl;
Box box4(15,30,29);//指定3个实参
cout << "the volume of box2 is" << box4.volume() << endl;
return 0;
}

使用默认参数的构造函数,即使在调用构造函数时没有提供实参值,不仅不会出错,而且还确保按照默认的参数值对对象进行初始化。

使用默认参数的构造函数,应该在声明构造函数时指定默认值,而不能只在定义构造函数时指定默认值。

注意:一个类中定义了全部是默认参数的构造函数后,不能在定义重载构造函数。

2.析构函数

(1).析构函数也是一个特殊的函数,其作用与构造函数相反,其名字是类名的前面加一个“~”符号,该符号称为“位取反运算符”。

(2).在以下几种情况下,程序会执行析构函数:

如果在一个函数中定义了一个对象(为自动局部对象),当这个函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。

static局部对象在函数调用结束时对象并不释放,因此也不调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用static局部对象的析构函数。

如果定义了一个全局对象,则在程序的流程离开其作用域时,调用该全局对象的析构函数。

如果用new运算符动态的建立一个对象,当用delete运算符释放该对象时,先调用该对象的析构函数。

(3).析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作,使这部分内存可以被程序分配给新对象使用。

(4).析构函数不返回任何值,没有数据类型,也没有函数参数,因此它不能被重载,一个类只能有一个析构函数。

(5).先构造的后析构,后构造的先析构。(有点像栈的思想。。。。。。)

3.对象数组:

对于一个类,要声明多个对象,就可以用到对象数组。如student类已经声明,定义stu数组,有50个元素就可以写成:student stu[50];如果构造函数只有一个参数,则可以在等号后面的花括号内提供实参,如student stu[3] = {60,70,78};但如果构造函数不是只有一个参数,那么就不能在定义数组时直接提供所有实参,以免弄混。

4.对象指针:

指针可以用来指向一般的变量,也可以用来指向对象。

(1.)对象的指针:对象空间的起始地址就是对象的指针,定义指向类对象的指针变量的一般形式为:

类名 *对象指针名

(2).指向对象成员的指针:存放对象初始地址的指针变量就是指向对象的指针变量;

       存放对象成员地址的指针变量就是指向对象成员的指针变量。

(3)指向对象成员数据的指针:

一般的定义方法为:数据类型名 * 指针变量名,int *p1;。如:假设Time类的数据成员hour为公用的整形数据,则可以在类外同过指向对象数据成员的指针变量访问该成员,p1 = &t1.hour;cout << "*p1" << endl;

(4)指向对象成员函数的指针:

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

      注意:在这里,前面的括号不能够省略掉,因为括号的优先级高于*,如果没有这个括号,就相当于

void Time :: *(p2());      p2 = &Time::get_time;

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

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

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

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

(5).this指针:前面已经介绍过this指针,在这里补充一点,return ((*this).height + (*this).width + (*this).length);

在这里,*this两侧的括号不能省略,因为成员运算符‘.’的级别高于指针运算符‘*’。


5.共用数据的保护:

(1).常对象:在定义对象时指定对象为常对象,如time const t1(12,34,46);则对象t1中的所有数据成员的值都不能被修改。

定义常对象的一般形式为:类名 const 对象名【(实参表列)】;

                                   或: const 类名 对象名【(实参表列)】。

如果一个对象被声明为常对象,则不能调用该对象的非const 型的成员函数(除系统自动调用的构造函数和析构函数)。为了能够引用常对象中的数据成员,要将成员函数声明为:const ,如:void get_time()const;这就使其成为了一个常成员函数,可以访问常对象中的数据成员,但不允许修改常对象中的数据成员的值。

如果一定要修改常对象中的某个数据成员的值,则可以对该数据成员声明为mutable,如mutable int count。

(2).常对象成员:

常数据成员:如果在类体中声明了常数据成员,只能通过构造函数的参数初始化表对常数据成员进行初始化,不能采用一般赋值的方法,因为常数据成员是不能被赋值的。

常成员函数:如果将成员函数声明为常成员函数,则只能引用本类中的数据成员,而不能修改他们。一般形式为:void get_time()const;const是函数类型的一部分,在声明函数和定义函数时都要有const关键字,在调用时不用加。

(3).指向对象的常指针:

将指向对象的指针变量声明为const型并将之初始化,这样指针值始终保持为其初值,不能改变,也就是说该指针的指向永远不变,但是可以改变其所指对象中的数据成员的值。

定义指向对象的常指针的一般形式为:类名 * const 指针变量名 = 对象地址;

(4).指向常对象的指针变量:

定义指向常变量的指针变量的一般形式为:const 类型名 *指针变量名;

如果一个变量已被声明为常变量,只能用指向常变量的指针变量去指向它;指向常变量的指针变量除了可以指向常变量外,还可以指向未被声明为const的变量,但是不能通过该指针变量改变该变量的值。

注意:定义了指向常变量的指针变量p,再使他指向c,并不意味着把c也声明为常变量,而只是在通过指针变量引用c时,c具有常变量的特征。

形参有const和无const的情况:

指向常对象的指针变量:
class Time
{
private:
    int hour;
    int minu;
public:
    Time(int h = 0,int m = 0):hour(h),minu(m){}
    void fun(const Time *p)const
    {
//        p->hour = 18;
        cout << p->hour << endl << p->minu << endl;
    }
};

int main()
{
    const Time t1(20,30);
    t1.fun(&t1);
    return 0;
}

//无论t1是不是const型对象,只要形参是const型,则传参后不能对数据进行修改,只能读取。
//如果一个对象被声明为常对象,则不能调用该对象的非const成员函数
//如果对象是const型,则形参一定要是const型,否则,编译不过,因为指向非const对象的指针是不能指向const对象的
//总之:当希望在调用函数时,对象的值不被修改,就应当把形参定义为指向常对象的指针变量,同时用对象的地址作实参。
//如果要求该对象不仅在调用过程中不被改变,而且在运行过程中也不被改变,则应将对象定义为const型。


(5).对象的常引用:

class Time
{
public:
    Time(int h = 0,int m = 0):hour(h),minu(m){}
    int hour;
    int minu;
};
void fun(Time t)
{
    t.hour = 18;
}
int main()
{
    Time t1(20,30);
    fun(t1);
    cout << t1.hour << endl;
    return 0;
}

对比上下两段代码有什么不同。

class Time
{
public:
    Time(int h = 0,int m = 0):hour(h),minu(m){}
    int hour;
    int minu;
};
void fun(Time &t)
{
    t.hour = 18;
}
int main()
{
    Time t1(20,30);
    fun(t1);
    cout << t1.hour << endl;
    return 0;
}

//上一段代码中,fun的形参是一个对象名,在函数中修改形参的值,因为在调用时建立了一个新的对象t,它是实参t1的拷贝,所以t的修改,对t1没有影响。
//在下面的代码中,形参是变量的引用名,在调用函数的过程中,把实参的地址传给形参,使引用名也指向实参变量,所以t中成员的改变会影响t1,
//如果不希望t1的值被修改,可以把引用t声明为const,即:void fun(const Time t);


6.对象的动态建立和释放:

用new运算符动态地创建对象,用delete运算符撤销对象。

用new运算符动态地分配内存后,将返回一个指向新对象的指针的值,即所分配的内存空间的起始地址。所以需要定义一个指向本类的对象的指针变量来存放该地址。

如:Time *pt;     pt = new Time;...........delete pt;

7.对象的赋值:运用赋值运算符,‘=’,实现同属于一个类的两个对象的赋值,一般形式为:对象名1 = 对象名2;

对象的赋值,只对其中的数据成员赋值,而不对成员函数赋值。

注意:类的数据成员中不能包括动态分配的数据,否则在赋值时可能出现严重后果。

8.对象的复制:Time t1(t2);,用已有的对象t2去克隆出一个新的对象t1。

对象复制的一般形式为:类名  对象2(对象1);用对象1复制出对象2

9.静态数据成员:

以关键字static开头,如:

class Time
{
private:
    static int hour;//把hour定义为静态的数据成员
    int minu;
    int s;
};



如果希望各个对象中的hour的值是一样的,就可以把它定义为静态的数据成员,这样,它就为各对象所共有,而不仅仅属于一个对象。

静态的数据成员在内存中只占一份空间。

注意:如果只声明了类而未定义对象,则类的一般数据成员是不占空间的,只有在定义对象时,才为对象的数据成员分配空间。但是,静态数据成员不属于某一个对象,所以在定义对象的时候开辟的空间也不包括该部分,它是在所有对象之外单独开辟空间。也就是说,只要声明了类,即使不定义对象,也为静态数据成员分配空间。

静态数据成员分配的空间,到程序结束时才释放空间。

静态数据成员可以初始化,但只能在类体外进行初始化,(系统默认初始为0)如:

int Time :: hour = 10;其一般形式为:数据类型  类名 :: 静态数据成员 = 初值;

注意:不能用参数初始化表对静态数据类型进行初始化

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

10.静态成员函数:

        在类中声明函数的static就成了静态成员函数,如:static int volume();

        静态成员函数是类的一部分,而不是对象的一部分,它与任何对象都无关,因此静态成员函数没有this指针,无法对一个对象中的非静态成员进行访问,但是可以直接引用本类的静态数据成员。如果要在类外调用公用的静态成员函数,要用类名和域运算符‘::’,如:Box :: volume();

(当然也允许通过对象名调用静态成员函数)。如果要引用本类的非静态成员,应该加对象名和成员运算符‘.’,如:cout << a.hour << endl;

        作用:静态成员函数不是为了对象之间的沟通,而是为了能够处理静态数据成员。


示例:计算前n个学生的平均成绩

#include <iostream>

using namespace std;

class Student
{
public:
    Student (int n,int a,float s):num(n),age(a),score(s) {}
    void total();
    //公用成员函数可以引用本对象中的非静态数据成员,也可以引用类中的静态数据成员
    static float average();
private:
    int num;
    int age;
    float score;
    static float sum;
    //定义这两个数据成员,一个记录所有学生的总成绩,一个记录学生的数目,
    //这两个数据都不是每个对象独有的,而是整个类公有的,对任何一个对象来说都是相同的
    static int coun;
};

void Student::total()
{
    sum += score;
    coun++;
}
float Student :: average()
{
    return (sum/coun);
}
float Student::sum = 0;//对公用静态数据成员进行初始化
int Student :: coun = 0;

int main()
{
    Student stud[3] =
    {
        Student(1001,18,70),
        Student(1002,19,78),
        Student(1003,20,98)
    };
    int n;
    cin >> n;
    for(int i = 0; i < n; i++)
    {
        stud[i].total();
    }
    cout << "the average score of " << n << " is" << Student :: average() << endl;
    return 0;
}
//附:如果想在average函数中引用stud[1]的非静态数据成员score,有两种解决办法:
//一种是将对象数组stud定义为全局数组,增加作用域
//另外一种是将对象作为average函数的形参,在调用该函数时将一个实参对象传给该函数。
//如:
float Student :: average(Student stu)
{
    cout << stu.score << endl;
    return (sum / coun);
}
//average函数的形参可以写成以下几种形式:
float Student :: average(Student &stu)//函数参数为对象的引用
float Student :: average(const Student stu)
float Student :: average(const Student &stu)


11.友元:

            友元包括友元函数友元类

(1).将普通函数声明为友元函数

            一个非成员函数,不属于任何类,如果想让该函数引用类中的私有成员,要在类的定义中声明该函数是类的友元函数,如示例:

class Time
{
private:
    int hour;
    int minu;
    int ss;
public:
    Time (int h,int m,int s):hour(h),minu(m),ss(s){}
    friend void display(Time &);//声明该函数是这个类的友元函数
    //注意:display函数并不是成员函数,如果没有声明,将不能引用
};
void display(Time &t)
{
    //在引用的过程中加上对象名,因为该函数不是成员函数,没有this指针
    cout << t.hour << ":" << t.minu << ":" << t.ss << endl;
}
int main()
{
    Time t1(10,13,56);
    display(t1);//调用普通函数
    return 0;
}


(2).友元成员函数

    friend函数不仅可以是一般的函数,还可以是另一个类中的成员函数。

示例:

class Date;
//这里用到了类的提前引用声明,Date类还没有定义,
//但是在该类定义之前需要先用到这个类,
//如果是先写Date类的声明,则同样要在这里先声明Time类
//但是,要知道只有在真正定义了类体之后才能够定义对象

class Time
{
private:
    int hour;
    int minu;
    int ss;
public:
    Time (int h,int m,int s):hour(h),minu(m),ss(s){}
    friend void display(Date &);//声明该函数是这个类的友元函数
    //注意:display函数是(Date)类的成员函数,形参是Date类对象的使用
};
class Date
{
private:
    int month;
    int day;
    int year;
public:
    Date(int m,int d,int y):month(m),day(d),year(y){}
    friend void Time::display(Date &);
    //在这里,要声明Time类中的display是这个类的友元函数
};
void Time :: display(Date &d)
{
    //同样,display函数是Time类的成员函数,所以在使用Date类的数据成员时,要注明对象。
    cout << d.month << ":" <<d.day << ":" << d.year << endl;
    cout << hour << ":" <<minu << ":" << ss << endl;
}
int main()
{
    Time t1(10,13,56);
    Date d1(12,25,2004);
    t1.display(d1);//display函数是Time类的成员函数,该函数中用到了Date的成员
    //所以实参一定是Date类的对象
    return 0;
}


(3).友元类

        如果B类是A类的友元类,则友元类B中的所有函数都是A类的友元函数,可以访问A类的所有成员

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

    注意:

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

    友元的关系是不能够传递的。    

12 类模板

        步骤:

        (1)在类声明前面加入一行,格式为:templaty <class 虚拟类型参数>

        (2)用类模版定义对象时用以下形式:

            类模板名<实际类型名>对象名

            类模版名<实际类型名>对象名(实参列表)

        (3)如果在类模板外定义成员函数,应写成类模板形式:

            templaty <class 虚拟类型参数>

            函数类型 类模版名 <虚拟类型参数>::成员函数名(函数形参表列){.....}

        说明:

            (1)类模板的类型参数可以有一个或多个,每个类型前面都必须加class,如

templaty <class T1,class T2>
    class someclass
    {
          
    }


            在定义对象时分别代入实际的类型名,如:

 someclass<int,double> t1;


                            (2)一个类模板可以作为基类,派生出派生类模板


  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值