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

【菜鸟C++学习笔记】26.构造函数

2012-10-25 22:51 375 查看
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指向这个内存空间,这样旧的成员指针和新的成员指针指向不同的内存区域,删除一个不会出现迷途指针的情况。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: