对于类,前面的文章分别简单讲述了什么是类,以及类的构造函数,类的复制构造函数的作用。前面文章的相关链接如下:
类的定义与初始化
复制构造函数与析构函数
那么下面将介绍组合类,什么是组合类?组合类有什么作用呢?通常我们在定义一个类的对象时,类成员的类型实际上通常为基本数据类型,如 int ,float,double…实际上类成员除了可以是基本的数据类型外,还可以是自定义数据类型,也就是说数据类型也可以是类的对象,那么如果定义一个组合类,那么类的构造顺序就很重要。利用前面提到过的构造函数对类进行初始化时,遵循以下构造顺序:
1.当类的成员类型为类的的对象时,首先初始化构造类成员数据。对类成员按照定义顺序进行构造,先定义先构造
2.构造完成员在构造自己
下面将举一个例子来说明问题,我们首先定义一个point类
class point
{
private:
int x, y;
public:
int getx() {
return x; }
int gety() {
return y; }
point(int xx = 0, int yy = 0) :x(xx), y(yy) {
cout << "calling point init constructor" << endl; }//构造函数
point(const point & p1);//复制构造函数
};
每每使用初始化构造函数时,输出相应的调试语句,该类还包含了整数 x y ,
复制构造函数定义如下
point::point(const point &p1) {
cout << "calling point copy constructor" << endl;
x = p1.x;
y = p1.y;
}
可以看到point类的复制构造函数就是将对应成员 x y 一一复制给另一个成员。
下面定义一个line 类,line中包含了这个point 类,对应的line定义如下
class line {
private:
double len;
//私有成员为point类
point p1, p2;
public:
double getline() {
return len; }
line(point xp1, point xp2);//构造函数
line(const line &p11);//复制构造函数
};
我们看到使用了组合类的方式,使用point类作为line中的数据成员我们来看一下其中构造函数的内容
line::line(point xp1, point xp2):p1(xp1),p2(xp2)
{
cout << "calling line init construct" << endl;
double x = static_cast<double>(p1.getx() - p2.getx());
double y = static_cast<double>(p1.gety() - p2.gety());
len = sqrt(x*x + y*y);
}
可以看到使用一个简单的初始化列表首先将point初始化,在根据两个point数据计算距离,并将值赋给 len。
看一下point的复制构造函数
line::line(const line &p11 )
{
cout << "calling line copy construct" << endl;
p1 = p11.p1;
p2 = p11.p2;
len = p11.len;
}
以上就是line与point全部数据以及构造函数,下面我们在主函数中调用并观察以下构造的顺序。代码如下:
# include <iostream>
# include <cmath>
using namespace std;
//类的组合,实际上我们在定义类的成员时通常会定义基础数据类型,实际上类的成员也可以是类的对象。
//类的组合描述的就是一个类内嵌其他类作为成员的情况。他们之间的关系是一种包含于被包含的关系
//值得注意的是类的构造顺序。
class point
{
private:
int x, y;
public:
int getx() {
return x; }
int gety() {
return y; }
point(int xx = 0, int yy = 0) :x(xx), y(yy) {
cout << "calling point init constructor" << endl; }//构造函数
point(const point & p1);//复制构造函数
};
point::point(const point &p1) {
cout << "calling point copy constructor" << endl;
x = p1.x;
y = p1.y;
}
//类的组合
class line {
private:
double len;
//私有成员为point类
point p1, p2;
public:
double getline() {
return len; }
line(point xp1, point xp2);//构造函数
line(const line &p11);//复制构造函数
};
//组合类的构造函数
line::line(point xp1, point xp2):p1(xp1),p2(xp2)
{
cout << "calling line init construct" << endl;
double x = static_cast<double>(p1.getx() - p2.getx());
double y = static_cast<double>(p1.gety() - p2.gety());
len = sqrt(x*x + y*y);
}
//组合类复制构造函数
line::line(const line &p11 )
{
cout << "calling line copy construct" << endl;
p1 = p11.p1;
p2 = p11.p2;
len = p11.len;
}
int main()
{
point p1(1, 1), p2(4, 5);
line line1(p1, p2);
line line2(line1);
cout << "the line 1 length is" << endl;
cout << line1.getline() << endl;
cout << "the line 2 length is" << endl;
cout << line2.getline() << endl;
return 0;
}
代码运行结果如下:
我们来简单分析一下程序运行的结果,我们首先再次提及什么时候用复制构造函数
1.当用一个已有的类初始化另一个类会调用初始化构造函数。
2.当作为函数的形参虚实结合时调用函数复制构造函数
3.当类的对象作为返回值调用复制构造函数。
int main()
{
point p1(1, 1), p2(4, 5);//初始化两个point 调用初始化point
line line1(p1, p2);//进入到line构造函数时虚实结合,调用了point复制构造函数,接着在初始化列表中,用已有的类成员初始化另一个数据成员再次调用初始化构造函数。最后调用line 初始化构造函数
line line2(line1);//进入到复制构造函数中先构造其中的两个point类,
//调用了其中复制构造函数
cout << "the line 1 length is" << endl;
cout << line1.getline() << endl;
cout << "the line 2 length is" << endl;
cout << line2.getline() << endl;
return 0;
}
以上分析了类的组合以及构造函数的次序,下面介绍类的前向引用声明,我们知道类是先声明后使用,当我们遇到两个类相互调用的情况需要类的前向引用声明。如下
class b;
class a{
void(b a1);
};
class b{
};
其中值得注意的是,虽然使用了前向引用声明,但是在提供一个完整的类定义之前,不能使用该类的细节,比如
class b;
class a{
b a;
};
class b{
};
这样做是不成立的,因为b不能给出类的完整定义,在定义一个数据类型时,至少应该知道占用几个字节吧?因为没有完整的细节,所以报错。
以上就是类的组合以及前向引用声明的使用。