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

本文详细介绍了构造函数和析构函数的概念、用途及实现方式。包括构造函数的自动调用特性、如何定义和使用带参数的构造函数、构造函数重载、使用默认参数的构造函数等内容。同时,也探讨了析构函数的特点及其与构造函数的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        一、构造函数:

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 ,此时指代的是当前对象;



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值