1、成员变量的初始化
成员变量除了可以在调用构造函数时将参数传递进去,同时也可以在定义的时候就初始化,方法是在构造函数右加冒号,跟成员变量名称和小括号,括号中写初始化的值。最后跟大括号写函数执行功能,如下面的例子:
#include <iostream>
using namespace std;
class A
{
public:
A():w(2),h(3){cout<<"长方形面积为:"<<w*h<<endl;}
private:
int w;
int h;
};
int main()
{
A one;
return 0;
}
输出结果:
分析:第六行表示成员变量初始化的方法。
作用:由于常量和引用只能被初始化,所以引入这种在构造函数对常量和引用进行初始化。
讨论:初始化顺序由类中成员变量生命的顺序决定,而析构的顺序与构造函数执行顺序相反,如下面的例子:
#include <iostream>
using namespace std;
class A
{
public:
A(){x=0;cout<<"类A的默认构造函数"<<x<<endl;}
A(int i){x=i;cout<<"类A的带一参数的构造函数"<<x<<endl;}
~A(){cout<<"类A的析构函数"<<x<<endl;}
int get(){return x;}
void set(int i){x=i;}
private:
int x;
};
class rectangle
{
public:
rectangle(){x=100;cout<<"类rectangle的默认构造函数"<<endl;}
rectangle(int i,int j,int k):x(i),w(j),l(k)
{
cout<<"类rectangle的带三个参数的构造函数"<<endl;
cout<<"长方形面积为:"<<l.get()*w.get()<<endl;
}
private:
A l;
A w;
int x;
};
int main()
{
rectangle a(100,200,300);
return 0;
}
输出结果:
分析:
1)rectangle类的两个成员变量是类A的两个对象w和h,这种东西叫“包含”
2)第18行定义了一个带三个参数的构造函数,同时对变量进行了初始化,值由第30行的值进行传递,然而执行的顺序并不是x->w->l
3)先构造了一个l对象,值为300,接着构造一个w对象,值为200,之后调用rectangle类的带三个参数的构造函数输出面积
4)析构顺序与构造顺序正好相反
结论:构造的顺序与类中成员变量的的顺序有关,与初始化无关!
3、调用构造函数进行类型转换
我们可以把数字作为对象赋给另一个对象,这样在对该赋值表达式进行计算时,首先要对数字进行类型转换,同时判断该类的构造函数的参数是否与数字类型匹配,匹配的话用构造函数创建一个临时对象,接着将这个临时对象赋给赋值操作符左边的对象,最后调用析构函数删除临时对象。例如:
#include <iostream>
using namespace std;
class A
{
public:
A(int i){x=i;cout<<"构造函数执行"<<x<<endl;}
~A(){cout<<"析构函数执行"<<x<<endl;}
void get(){cout<<x<<endl;}
private:
int x;
};
int main()
{
A a(10);
a.get();
a=100;//强制类型转换,将100转换为类A的临时对象,将这个对象赋给对象a,之后调用析构函数析构临时对象
a.get();
a=A(1000);//第二种强制类型转换,调用A的构造函数创建临时对象,将1000作为A成员x的值,然后把这个临时对象赋给对象a,之后调用析构函数析构临时对象
a.get();
A b(10000);
return 0;
}
输出结果:
分析:
1)首先创建对象a,并由构造函数初始化值为10
2)第一次强制转换,创建临时对象,100赋给a后,析构这个临时对象
3)第二次强制转换,创建临时对象,1000赋给a后,析构这个临时对象
4)创建另一个对象b,调用构造函数初始化为10000,
5)先析构对象b,再析构对象a
4、浅层复制构造函数与深层复制构造函数
所有复制构造函数的参数都是对同一个类的对象的引用,即:
A(A&a)
由于需要复制的对象一般不会更改,因此通常定义为常量引用,即:
A(const A&a)
这个函数根据对象a生成一个副本,a是形式参数,代表传递进来的对象,即对象的外号。
假如原对象里有一个指针指向堆中的一个空间,那么复制的对象里也会有指向同一块区域的指针,那么删除第一个指针指向的内存空间时,复制对象里的指针将成为迷途指针,导致出错。我们把这种复制方式成为浅层复制。
为了防止浅层复制导致迷途指针的问题,我们需要自己创建复制构造函数,并在函数中为成员指针指向的数据成员分配内存,这样两个对象的数据成员都拥有各自的内存区域,这样,一个对象析构后不会影响另一个,这种方式称为深层复制。如下面的例子:
#include <iostream>
using namespace std;
class A
{
public:
A(){x=new int;*x=10;}
~A(){delete x;x=NULL;}
A(const A&a)
{
cout<<"复制构造函数执行"<<endl;
x=new int;
*x=*(a.x);
}
void get(){cout<<*x<<endl;}
void set(int i){*x=i;}
private:
int *x;
};
int main()
{
A *p=new A();
p->get();
A b=(*p);
p->set(20);
b.get();
b.set(30);
p->get();
delete p;
return 0;
}
输出结果:
分析:
经过这个处理后,复制构造函数创建对象时同时创建一个新空间,并用x指向这个内存空间,这样旧的成员指针和新的成员指针指向不同的内存区域,删除一个不会出现迷途指针的情况。