您的位置:首页 > 编程语言 > C语言/C++

#3:C++:类和对象的进一步讨论;

2018-02-06 07:23 323 查看
        一、构造函数:

1. 一个对象的初始化:

    在建立一个对象的时候,常常需要一些初始化的操作:赋初值,初始化等;

注:

    不能在声明类类型的同时进行初始化:

    

class Time    //    错误!!

{

    hour = 0;     

};                

                              

    而且仅仅在数据为public的情况下可以在定义对象的时候赋初值!!!:

    

class Time

{... } time1 = { 13,45,23 };

    两个值之间通过逗号分隔开;

2. 构造函数的说明:

    1. 构造函数是一种特殊的函数,用户不需要亲自去调用它,而是在建立对象的同时自动执行!

    

     2. 构造函数的名字必须和class 的名字相同,而不能由用户自己定义函数名;

    

    3. 构造函数不具有任何返回值,不具有任何类型;

    

    4. 用户需要自己定义函数体和函数参数变量表;

例: 类内定义构造函数:


class Time
{
private:
int hour;
int minute;
int sec;

public:
void SetTime();
void ShowTime();
Time()
{
hour = 0;
minute = 0;
sec = 0;
}
};

void Time :: SetTime()
{
cout << "Please Input the Time :" << endl;
cin >> hour >> minute >> sec;
}

void Time :: ShowTime()
{
cout << hour << ':' << minute << ':' << sec << endl;
}

int main()
{
Time time1;
time1.ShowTime();

time1. SetTime();
time1.ShowTime();

return 0;
} 在此例中,构造了一个构造函数,在建立对象的同时,自动执行这个函数,为各个变量赋初值;
例: 类外定义构造函数:
class Time
{
private:
int hour;
int minute;
int sec;

public:
void SetTime();
void ShowTime();
Time();
};

Time :: Time()
{
hour = 0;
minute = 0;
sec = 0;
}

void Time :: SetTime()
{
cout << "Please Input the Time :" << endl;
cin >> hour >> minute >> sec;
}

void Time :: ShowTime()
{
cout << hour << ':' << minute << ':' << sec << endl;
}

int main()
{
Time time1;
time1.ShowTime();

time1. SetTime();
time1.ShowTime();

return 0;
} 构造函数使用的几点说明:

    1. 是在类对象进入作用域时,开始调用构造函数,即对象开始被使用时;

    

    2. 构造函数没有返回值,所以在声明前加 void 也是错误的;

    

    3. 构造函数不能被用户自己调用,必须是在成员生命周期开始时自己调用,且仅执行一次;

    

    4. 如果用户自己没有定义构造函数,则系统会自动生成一个构造函数,但函数体为空;

       带参数的构造函数:

    一般格式 :        构造函数名(类型1 形参1,……)

注:

    真正的实参是在定义对象的时候给出的,此时定义对象的格式:

    

    class     类名    对象名(实参1,……);

例:
class Box
{
public:
Box(int ,int ,int );
int volume();

private:
int length;
int width;
int height;

};

Box :: Box(int l,int w,int h)
{
length = l;
width = w;
height = h;
}

int Box :: volume()
{
return (length * width * height);
}

int main()
{
Box box1(12,25,20);
cout << "The volume of this box is :" << box1.volume() << endl;

Box box2(15,30,21);
cout << "The volume of this box is :" << box2.volume() << endl;

return 0;
}
说明:
    1. 带参构造函数中的形参,其对应实参在定义对象的时候给定;

    

    2. 此时可以对不同的对象进行不同的初始化;

       使用参数初始化表对数据成员初始化:

例:

Box :: Box(int l,int w,int h) : length(l),width(w),height(h){}// 后无分号;

在原来的函数首部加上冒号,然后列出初始化表;

       构造函数的重载:

    在同一个类中可以定义多个构造函数,而参数的类型或个数不同;

例:
class Box
{
public:
Box();
Box(int l,int w,int h): length(l),width(w),height(h) {}

int volume();

private:
int length;
int width;
int height;

};

Box :: Box()
{
length = 10;
width = 10;
height = 10;
}

int Box :: volume()
{
return (length * width * height);
}

int main()
{
Box box1;
cout << "The volume of this box is :" << box1.volume() << endl;

Box box2(15,30,21);
cout << "The volume of this box is :" << box2.volume() << endl;

return 0;
}
  在此类中定义了两个构造函数:

        第一个构造函数无参数:    Box :: Box() { ... };

        是在函数体中对私有变量进行赋值的;

        

        第二个
构造函数是直接在类体中定义的:
          Box(int l,int w,int h): length(l),width(w),height(h) {}

        有三个参数,是用参数初始化表对成员进行初始化;

       
说明:

        1. 在调用时不必给出实参的构造函数,称为默认构造函数;

        

        2. 当使用无参构造函数进行对象定义的时候,对象书写的语句应当正确:

        

          Box box1;

          

    而:

          Box box1(); 是错误的,此时为声明一个普通函数其返回值为 Box型;

        

        3. 在一个类中可以包含多个构造函数,但是系统只会执行一条; 

       使用默认参数的构造函数:

    同之前的函数相同,构造函数也可以定义为含有默认参数的:

例:
class Box
{
public:
Box(int l = 10,int w = 10,int h = 10);		// 使用默认参数;
int volume();

private:
int length;
int width;
int height;

};

Box :: Box(int l,int w,int h): length(l),width(w),height(h) {};

int Box :: volume()
{
return (length * width * height);
}

int main()
{
Box box1;
cout << "The volume of this box is :" << box1.volume() << endl;

Box box2(15);
cout << "The volume of this box is :" << box2.volume() << endl;

Box box3(15,30);
cout << "The volume of this box is :" << box3.volume() << endl;

Box box4(15,30,20);
cout << "The volume of this box is :" << box4.volume() << endl;
return 0;
}
注:

    实参和默认变量的结合是从左向右,所以应当把设为默认参数的形参放在函数声明的末尾;

       析构函数:

    析构函数也是一个特殊的成员函数,作用与构造函数相反,函数名为类名前加 ~ ;

    

    当对象的生命期结束的时候,系统将自动执行析构函数:
情况有:

    1. 在某个函数中定义了一个对象(自动全局变量),当这个函数被调用时,对象应当释放,在释放前执行;

    

    2. static 型变量在函数结束时也不释放,而是在main函数 或执行exit函数结束时,才调用函数;

    

    3. 对于一个全局变量,在函数的流程超过其作用域的时候(main exit 结束时),调用析构函数;

    

    4. 用 new 动态分配建立一个对象后,当用delete删除时,首先调用析构函数

注:

    1. 析构函数的作用不是释放空间,而是在释放空间前,执行一些清理工作;

    2. 析构函数不返还任何值,没有函数类型,也没有函数参量,所以也不能被重载;

        

        所以一个类可以有多个构造函数,但是只能有一个析构函数;

        

    

    3. 析构函数其实是执行:用户希望在最后一次使用对象之后所执行的任何操作(输出信息等);

    

    4. 如果用户没有定义析构函数,同样的,系统也会自动生成一个空函数体的析构函数;

例:
class Student
{
public:
Student(int ,string ,char );	//	构造函数
~Student();			//	析构函数

void Display();

private:
int num;
string name;
char sex;
};

Student :: Student(int xuehao,string ming,char xingbie)
{
num = xuehao;
name = ming;
sex = xingbie;
cout << "The constructor is called !" << endl;
}

Student :: ~Student()
{
cout << "The Disconstructor is called !" <<  num  <<endl;
}

void Student :: Display()
{
cout << "The number is :" << num << endl;
cout << "The name is :" << name << endl;
cout << "The sex is :" << sex << endl;
}

int main()
{
Student stu1(100,"ZK",'M');
stu1.Display();

Student stu2(101,"ZKK",'M');
stu2.Display();

return 0;
}
注:    调用构造函数和析构函数的顺序:

    

    先构造的后析构,后构造的先析构!!!!!!!!( 在同一个函数中定义的局部变量,作用域相同 )

    

    1. 在全局中定义的对象(所有函数之外),他的构造函数在所有函数执行之前被调用;

    

    注:对于一个程序中多个文件中都定义了全局变量,此时构造函数的调用是不确定的;

    

        当main 或 exit 执行后,调用析构函数;

        

    2. 如果定义的是局部变量(在某函数中),则在每一次建立对象时调用其构造函数。

    

    如果函数被多次调用,则每一次建立对象时都需要调用构造函数;

    

        当函数调用结束、对象释放时,先调用析构函数;

        

    3. 如果在函数中定义静态static局部对象,则只在程序第一次调用此函数时调用构造函数;

        

    在函数调用结束时,对象并不释放,也不调用析构函数;

        

        当main 或 exit 执行后,调用析构函数;

例:
    void fn()

    {

        Student stu1;

        static Student stu2;

    }

    在调用 fn 函数时,先调用 stu1的构造函数,在执行stu2的构造函数;

    

    在 fn 调用结束时,stu1 需要释放,此时调用 stu1的析构函数;

        而stu2 是静态局部变量,只有在 main 结束时才释放,此时调用 stu2 的析构函数;

        

       对象数组:

1. 定义:    Student stud[50];

2. 构造函数如果只有一个参数:

例:

    Student :: Student(int num);

    

此时可以直接在等号后面提供实参:

    Student stud[3] = {60,61,62};

3. 构造函数有多个参数:

例:


    Student :: Student(int ,int ,int );

    

此时,3个参数分别为三个元素的第一个实参,即:

    编译系统只会为每一个对象元素的构造函数提供一个实参!!!

    

    

此时可以定义:

    Student stud[3] = {

    

        Student(1001,18,88);

        Student(1002,11,22);

        Student(1003,13,33);

    };

即在建立对象数组时,分别调用构造函数,对每个元素初始化;

例:
class Box
{
public:
Box(int l,int w,int h) : length(l),width(w),height(h){}

int volume();

private:
int length;
int width;
int height;
};

int Box :: volume()
{
return (length * width * height);
}

int main()
{
Box box[3] = {

Box(10,10,10),
Box(10,10,15),
Box(10,15,10),
};

for(int i = 0;	i < 3 ;	i++)
{
cout << "The volume of the box " << i << " is " << box[i].volume() << endl;
}
cout << endl;
return 0;
}
  
对象指针:
     
一、指向对象的指针:


        
建立对象时,编译系统会为每一个变量分配空间,而对象空间的起始地址就是变量名;
例:

    Time *pt;

    Time t1;

    pt = &t1;    //    将 Time 类型的变量 t1 的起始地址作为指针给 pt;

    

此时可以通过指针访问变量(public)

    

    (*pt).hour

    pt->hour

    (*pt).get_time();

    pt->get_time();

      二、指向对象成员的指针:

1. 指向对象数据成员的指针:

    此时和一般的数据指针的定义相同:

        int *pt = &t1.hour;

        cout << *pt << endl;

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

指向普通函数的指针变量的方法:

    

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

    void (* p)();    //    p 是指向void型函数的指针变量;

此时可以使他指向一个函数:

    p = fun;

    (*p)() ;    //    调用 fun 函数;

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

注:    p = t1.get_time;    //    错误,编译系统要求赋值语句中左右两侧的类型相匹配;

    而此时 p 为普通指针与 Time 类型无关,但是 get_time() 为 Time 型;

    

所以要求在以下三个方面都匹配:

        

        1. 函数参数的类型和参数个数;

        

        2. 函数返回值的类型;

        

        3. 所属的类;

        

应当区别普通函数和成员函数的不同性质:

    void (Time :: *p2)();    //    定义一个指向 Time 类 返回值为 void 公用成员函数的指针;

    p2 = &Time :: get_time;

    
注:

    定义括号不可省略:

    

否则成为:    void Time :: *(p2());    //    返回值为 void 型指针的函数;

定义的一般形式为:

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

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

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

例:
class Time
{
public:
Time(int ,int ,int );
int hour;
int minute;
int sec;

void ShowTime();
};

Time :: Time(int h,int m,int s)
{
hour = h;
minute = m;
sec = s;
}

void Time :: ShowTime()
{

cout << "The time is : " << hour << " : " << minute << " : " << sec <<endl;
}

int main()
{
Time t1(12,14,30);
int *p1 = &t1.hour;

cout << "The hour is : " << *p1 << endl;	//	定义一个指向 int 成员的普通指针;

t1.ShowTime();								//	成员调用函数;

Time *p2 = &t1;
p2->ShowTime();								//	定义一个指向对象的指针,并用指针指向成员函数

//	void (Time :: *p3) () = &ShowTime;
void (Time :: *p3) ();						//	定义一个指向成员函数的指针;
p3 = &Time :: ShowTime;
(t1.*p3) ();								//	用成员符号调用函数指针;

return 0;
}

注:    成员函数是存放在对象空间外的空间中的,多个同类对象共用一个代码段,

        所以赋给指针的应当是公用函数代码段的入口地址;

      this
指针:


        对于 n 个不同的对象,系统应当分配 n 个不同的空间,但是,他们共有相同的函数段;        

                

        那么,当不同对象的成员函数调用函数是,是怎么保证引用的是所指定的成员数据的呢?

        

    其实在每一个成员函数中包含着一个特殊的指针 this

        

            他是指向本类的指针,它的值就是当前被调用的成员函数所对应的对象的起始地址;

            

所以,当系统调用如:a.volume 函数时:

    编译系统就会把 a 的起始地址给 this ,此时this 找到相应的成员函数;

    
    实际上是执行 : (this -> length)*(this -> width)*(this -> height);

    

    当调用 b.volume 时,编译系统又将 b 的起始地址给 this 完成操作;

    
注:

    this 指针是作为隐式来使用的,它是作为参数被传递给成员函数的:


    

对于函数:

    int Box :: volume()

    {

        return (length)*(width)*(height);

    }

    
在 c++ 中被处理为:

    int Box :: volume(Box *this)

    {

        return (this -> length)*(this -> width)*(this -> height);

    }

                        

而在调用时,实际上是:

    a.volume(&a);    从而将 a 的地址传给 this ;

    

在使用时,也可以使用显式的this ,此时指代的是当前对象;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: