目的:为了使用户定义的数据类型如同一般数据类型一样使用。
功能:对对象进行初始化,有若干种方法。
特点:1、无返回值说明;2、不能继承;3、可以有缺省参数;4、不能取地址,构造时自动给对象分配栈中内存,析构时回收;
5、自动调用构造函数。
构造函数是类的一个特殊成员函数,其函数名同类名一样。C++通过构造函数可以正确地初始化对象。构造函数不能被显式调用,不能使
虚函数。
例: class queue {
int q[100]; //默认为private
int sloc,rloc;
public:
queue();
void qput(int i);
};
queue ::queue () //隐式调用
{
sloc=rloc=0;
}
int queue ::qput (int i){}
main()
{
queue a,b;
a.qput (10);
b.qput (20);
}
参数化的构造函数:
queue ::queue (int vx,int vy){} };
main()
{
queue a(10,20);
}
缺省参数的构造函数:特殊情况下需要传递参数,一般都是用缺省参数。
单个参数:queue ::queue (int i=16) {} };
main()
{
queue a;
}
多个参数:queue ::queue (int vx=0,int vy=0) {}
缺省参数还可用于一般成员函数。使用时应注意避免二义性。
多构造函数:
public:
queue ( );
queue (int );
queue (int,char );
};
main()
{
queue a;
queue b(1);
queue c(1,'c'); //避免二义性
}
拷贝构造函数:
1、系统产生:
queue ::queue (int vx,int vy){} };
main() { queue a(b) ; }
2、自定义:
queue ::queue (const queue &p){} };
main() { queue }
总结:构造函数的作用是对对象本身做初始化工作,也就是给用户提供初始化类中成员变量的一种方式。
如果一个类中没有定义任何的构造函数,那C++编辑器将在某些情况下提供一个默认的构造函数(不带参数),3种情况:
1)、类有虚拟成员函数或虚拟继承父类(虚拟基类);
2)、类的基类有构造函数;
3)、类中的所有非静态饿对象数据成员,它们所属的类中有构造函数。
构造函数的目的是为了初始化对象,因此一个构造函数至少应该
使得对象处于明确定义的状态。
例://class string
string () {s=new char[80];len=80;}
string (int n) {s=new char[n];len=n;}
}
string::stringprintf() { cout<< s <<endl;}
定义对象:
string x,y(80);
x.print();
y.print();
此时,x和y调用的print()函数结构未定义,因为在构造函数中只对字符数组分配了内存,却未对分配的内存进行初始化。
我们可以通过修改带默认参数值的构造函数来改进:
string (int n=80) {s=new char[n];s[0]='\0';len=n;}
用默认参数的形式来代替函数重载的形式。
注意点:
1)构造函数应该使对象处于明确定义的状态;
2)保持物理状态的一致性:对数据成员的定义保持一致,在所有函数中只能使用一种定义。
3)类不变性:可以将不变性作为程序代码的注释,//len=strlen(s);
4)动态内存的一致性:接口一致性
void assign (char* str) { strcpy(s,str);}
void concat (string& a) {s=new char[len+1];strcpy(s,a.s);}
两函数的表现行为存在不一致性:前者内存不再分配,而后者一直在分配。我们应只使用一种以保持一致性。
5)内存泄露:concat函数中每拷贝一次,s就重新分配一次,s被新的指针值覆盖,而前一指针值被抛弃,产生内存垃圾。
因此concat函数必须保证旧的数组一定要被删除,对于每一个new,就必须有一个delete操作,且delete语句只能被增加在
新的字符串创建之后。
void concat (string& a) {new_s=new char[len+1];strcpy(s,a.s); delete[]s;s=new_s;}