一、对象的初始化
在建立一个对象时,常常需要作某些初始化的工作。错误的示例:如下给一个对象t1赋值。
#include<iostream>
using namespace std;
class Time
{
private:
int hour;
int minute;
int sec;
};
int main( )
{
Time t1={14,56,30};//错误的初始化方式,编译会报错
return 0;
}
(一)使用构造函数初始化对象
构造函数(constructor)是一种特殊的成员函数。
构造函数不需要用户来调用它,而是在建立对象时自动执行。
构造函数的名字必须与类名同名,而不能由用户任意命名。
构造函数不具有任何类型,不返回任何值。
构造函数的功能是由用户定义的,用户根据初始化的要求设计函数体和函数参数。
构造函数内的对于数据成员的“赋值(初始化)”,不用带数据成员的类型。
1、类内定义构造函数
#include <iostream>
using namespace std;
class Time //时间类
{
public:
Time( ) //类内定义构造函数
{
hour=0; //注意:不用带参数类型
minute=0; //注意:不用带参数类型
sec=0; //注意:不用带参数类型
}
void set_time( );
void show_time( );
private:
int hour;
int minute;
int sec;
};
void Time::set_time( )
{
cin>>hour;
cin>>minute;
cin>>sec;
}
void Time::show_time( )
{
cout<<hour<<":"<<minute<<":"<<sec<<endl;
}
int main( )
{
Time t1;
t1.show_time( );
Time t2;
t2.set_time( );
t2.show_time( );
return 0;
}
2、类外定义构造函数
class Time
{
public:
Time();
void set_time();
void show_time();
private:
int hour;
int minute;
int sec;
};
Time::Time()
{
hour=0;
minute=0;
sec=0;
}
(二)无参&带参构造函数(构造函数重载)
注:无参构造函数,只是不需要输入参数,但其初始化值仍在构造函数内定义。如本例中,无参构造函数Time();将类内的数据成员初始化值为0。
class Time
{
public:
Time(); //无参数的构造函数
/*构造函数的重载(函数名相同,参数个数或类型不同)*/
Time(int,int,int); //有参数的构造函数,形参可以省略
void show_time();
private:
int hour;
int minute;
int sec;
};
Time::Time() //无参数的构造函数
{
hour=0;
minute=0;
sec=0;
}
Time::Time(int h,int m,int s) //有参数的构造函数
{
hour=h;
minute=m;
sec=s;
}
void Time::show_time( )
{
cout<<hour<<":"<<minute<<":"<<sec<<endl;
}
int main()
{
Time t1; //注意:此处会调用,无参数的构造函数
t1.show_time();
Time t2(15,39,59); //注意:此处会调用,有参数的构造函数
t2.show_time();
return 0;
}
(三)"参数初始化表"构造函数
优点:不在函数体内对数据成员初始化,而是在函数首部实现。当需要初始化的数据成员较多时更显其优越性。
1、类内使用参数初始化列表
构造函数名(无参/带参):数据成员(形参){};
class Time
{
public:
Time():hour(0),minute(0),second(0){}; //无参构造函数
Time(int h,int m,int s):hour(h),minute(m),second(s){}; //带参构造函数
void show_time();
private:
int hour;
int minute;
int sec;
};
2、类外使用参数初始化列表
类名::构造函数名(无参/带参):数据成员(形参){};
Time::Time(int h,int m,int s):hour(h), minute(m), sec(s){};
注意:
在构造函数的函数体中不仅可以对数据成员赋初值,而且可以包含其他语句;但一般不提倡在构造函数中加入与初始化无关的内容,以保持程序的清晰。
在类的成员函数中慎用输入输出,一旦如此,所有对象都会包含同样的输入输出。
(四)对象数据成员的赋值vs初始化的比较
初始化仅仅是在对象的声明时的操作,而赋值是在函数执行时的操作。
1、赋值操作
#include <iostream>
using namespace std;
class Student
{
private:
int num;
char name[20];
char sex;
public:
void set_data(int n, char *p,char s)//用于对数据成员赋值的成员函数
{
num=n;
strcpy(name,p);
sex=s;
}
void display( )
{
cout<<"num: "<<num<<endl;
cout<<"name: " <<name<<endl;
cout<<"sex: " <<sex<<endl<<endl;
}
};
int main()
{
Student stud1,stud2;
stud1.set_data(1,"He",'f'); //对stud1进行赋值操作
stud2.set_data(2,"She",'m'); //对stud2进行赋值操作
stud1.display();
stud2.display();
return 0;
}
2、初始化操作
#include <iostream>
#include<string.h>
using namespace std;
class Student
{
private:
int num;
char name[20];
char sex;
public:
Student(int n,char *m,char s)
{
num = n;
strcpy(name,m);
sex = s;
}
void display( )
{
cout<<"num: "<<num<<endl;
cout<<"name: " <<name<<endl;
cout<<"sex: " <<sex<<endl<<endl;
}
};
int main()
{
Student stud1(1,"He",'f'); //对stud1进行初始化
Student stud2(2,"She",'m'); //对stud2进行初始化
stud1.display();
stud2.display();
return 0;
}
(五)私有数据成员包含字符数组和字符串的初始化比较
1、私有数据成员包含字符数组
例1.1 类外带参构造函数进行初始化
#include <iostream>
#include<string.h>
using namespace std;
class Student
{
private:
int num;
char name[20];
char sex;
public:
Student(int n,char *m,char s);
void display( )
{
cout<<"num: "<<num<<endl;
cout<<"name: " <<name<<endl;
cout<<"sex: " <<sex<<endl<<endl;
}
};
Student::Student(int n,char *m,char s)
{
num = n;
strcpy(name,m);
sex = s;
}
int main()
{
Student stud1(1,"AAA",'f');
Student stud2(2,"BBB",'m');
stud1.display();
stud2.display();
return 0;
}
例1.2 类外带参构造函数使用参数列表初始化(特殊)
注意如下用法
Student::Student(int n,char *m,char s):num(n),sex(s){strcpy(name,m);};
// Example program
#include <iostream>
#include<string.h>
using namespace std;
class Student
{
private:
int num;
char name[20];
char sex;
public:
Student(int n,char *m,char s);
void display( )
{
cout<<"num: "<<num<<endl;
cout<<"name: " <<name<<endl;
cout<<"sex: " <<sex<<endl<<endl;
}
};
Student::Student(int n,char *m,char s):num(n),sex(s){strcpy(name,m);};
int main()
{
Student stud1(1,"AAA",'f');
Student stud2(2,"BBB",'m');
stud1.display();
stud2.display();
return 0;
}
2、私有数据成员包含字符串
例2.1:类内带参构造函数初始化
#include <iostream>
using namespace std;
class Student
{
private:
int num;
string name;
char sex;
public:
Student(int n,string m,char s)
{
num = n;
name = m;
sex = s;
}
void display( )
{
cout<<"num: "<<num<<endl;
cout<<"name: " <<name<<endl;
cout<<"sex: " <<sex<<endl<<endl;
}
};
int main()
{
Student stud1(1,"AAA",'f');
Student stud2(2,"BBB",'m');
stud1.display();
stud2.display();
return 0;
}
例2.2:类外带参构造函数(参数列表法)初始化
#include <iostream>
using namespace std;
class Student
{
private:
int num;
string name;
char sex;
public:
Student(int,string,char);
void display( )
{
cout<<"num: "<<num<<endl;
cout<<"name: " <<name<<endl;
cout<<"sex: " <<sex<<endl<<endl;
}
};
Student::Student(int n,string m,char s):num(n),name(m),sex(s){};
int main()
{
Student stud1(1,"AAA",'f');
Student stud2(2,"BBB",'m');
stud1.display();
stud2.display();
return 0;
}
二、默认构造函数
调用构造函数时不必给出实参的构造函数,称为默认构造函数(default constructor)。
class Time
{
public:
Time( );
void show_time();
private:
int hour;
int minute;
int sec;
};
Time::Time( )
{
hour=0;
minute=0;
sec=0;
}
int main()
{
Time t1; //非t1();
t1.show_time();
}
构造函数的使用说明:
如果用户自己没有定义构造函数,则C++系统会自动生成一个默认构造函数,只是这个构造函数的函数体是空的,也没有参数,不执行初始化操作。
即使提供了其他构造函数,提供一个默认构造函数几乎总是对的。
通常,在默认构造函数中,给成员提供的初始值应该指出该对象是“空”的。
三、带默认参数的构造函数(声明时形参有值)
使用默认参数的构造函数说明:
1)在声明构造函数时指定默认值,而不是在定义构造函数时指定默认值。
2)声明时,构造函数的形参名可以省略。
3)构造函数的声明和定义需一一对应。
注意1:
Time();
Time(int h,int m=0,int s=0);
这两个重载函数不冲突
注意2:
Time();
与
Time(int h=0,int m=0,int s=0);等冲突重载
#include <iostream>
using namespace std;
class Time
{
public:
Time(); //无默认参数的构造函数
Time(int,int=0,int=0);//带默认参数的构造函数;声明时,省略了构造函数的形参名
//上一行等同于 Time(int h,int m=0,int s=0);
void show_time();
private:
int hour;
int minute;
int sec;
};
Time::Time( ) : hour(0), minute(0), sec(0){ };//对应 声明 Time();
/*对应声明 Time(int,int=0,int=0); */
Time::Time(int h,int m,int s): hour(h), minute(m), sec(s){ };
void Time::show_time()
{
cout<<hour<<":"<<minute<<":"<<sec<<endl;
}
int main( )
{
Time t1; //使用默认构造函数初始化
t1.show_time();
Time t2(8); //使用带参数的构造函数初始化,仅一个参数,所以仅对数据成员1初始化
t2.show_time();
Time t3(8,30); //使用带参数的构造函数初始化,两个参数,所以依次对数据成1&2初始化
t3.show_time();
Time t4(18,56,48); ////使用带参数的构造函数初始化,三个参数,依次对全部数据成员初始化
t4.show_time();
return 0;
}

Time(int h,int m=0,int s=0);等同于Time(int,int=0,int=0);
避免由于默认参数引起歧义,如下面两个重载构造函数非法
Time();
Time(int h=12,int=0,int=0);
四、析构函数
(一)析构函数定义
析构函数(destructor)也是一个特殊的成员函数。
构造函数在对象创建时自动执行;当对象的生命期结束时,会自动执行析构函数。
析构函数的名字是类名的前面加一个“~”符号。
Time::Time(); //构造函数
Time::~Time(); //析构函数
析构函数的作用在撤销对象占用的内存之前完成一些清理、善后的工作。
只要对象的生命期结束,程序就自动执行事先设计好的析构函数来完成相关工作。
#include <iostream>
using namespace std;
class Student
{
public:
Student(int n,string nam,char s)
{
num=n;
name=nam;
sex=s;
cout<<"执行构造函数:"<<name<<" come."<<endl;
}
~Student( )
{
cout<<"执行析构函数:"<<"Bye bye, "<<name<<"."<<endl;
}
void display();
private:
int num;
string name;
char sex;
};
void Student::display( )
{
cout<<"num: "<<num<<endl;
cout<<"name: "<<name<<endl;
cout<<"sex: "<<sex<<endl<<endl;
}
int main( )
{
Student stud1(10010,"Wang_li",'f');
stud1.display( );
Student stud2(10011,"Zhang_fun",'m');
stud2.display( );
return 0;
}

(二)不同对象执行析构函数的时机
对于函数中定义的自动局部对象,当函数被调用结束时对象释放,在对象释放前自动执行析构函数。
static局部对象只在main函数结束或调用exit函数结束程序时,调用static局部对象的析构函数。
对于全局对象,在程序的流程离开其作用域时(如main函数结束或调用exit函数) 时,调用该全局对象的析构函数。
1、new和delete的配对
若在构造函数中用new运算符为对象成员动态地分配了空间,要在析构函数中,用delete运算符释放分配的空间。举例如下:
class ClassName
{
private:
int *p;
public:
ClassName();
~ClassName();
};
ClassName::ClassName()
{
p = new int;
}
ClassName::~ClassName()
{
delete p;
}
2、关于析构函数
析构函数不返回任何值,没有函数类型,也没有函数参数。
析构函数不能被重载:一个类可以有多个构造函数,但只能有一个析构函数。
析构函数的作用并不仅限于释放资源方面:它还可以被用来执行“用户希望在最后一次使用对象之后所执行的任何操作”。
析构函数可以完成类的设计者所指定的任何操作:一般情况下,类的设计者应当定义析构函数,以指定如何完成“清理”的工作。
如果用户没有定义析构函数,C++编译系统会自动生成一个析构函数:自动生成的析构函数只是徒有析构函数的名称和形式,实际上什么操作都不进行。
五、构造函数和析构函数的调用顺序



2339

被折叠的 条评论
为什么被折叠?



