C++类的组合、前向引用声明

3.5类的组合

Part1.应用背景

对于复杂的问题,往往可以逐步划分为一系列稍微简单的子问题。

解决复杂问题的有效方法是将其层层分解为简单的问题组合,首先解决简单问题复杂问题也就迎刃而解了。

在面向对象的程序设计中,可以对复杂对象进行分解、抽象,把一个复杂对象分解为简单对象的组合,由比较容易理解和实现的部件对象装配而成。

Part2.定义及代码

①定义:类的组合描述的就是一个雷内嵌其他类的对象作为成员的情况,他们之间的关系是一种包含与被包含的关系。

②作用机制:当创建类的对象时,如果这个类具有内嵌对象成员,那么各个内嵌对象将首先被自动创建。

      在创建对象时既要对本类的基本类型数据成员进行初始化又要对内嵌对象成员进行初始化。

③一般形式:

类名::类名(形参表):内嵌对象1(形参表),内嵌对象2(形参表),......
//内嵌对象1(形参表),...称作 初始化列表:对内嵌对象进行初始
{
          类的初始化
}

④组合类的构造函数、析构函数调用顺序

构造函数调用顺序:

        1)调用内嵌对象的构造函数,调用顺序按照内嵌对象在组合类的定义中出现的次序。

         注意:内嵌对象在构造函数的初始化类表中出现的顺序与内嵌对象构造函数的调用顺序无关。

        2)执行本类构造函数的函数体。

析构函数调用顺序:

        析构函数的调用执行顺序与构造函数刚好相反。

例:

#include<iostream>
using namespace std;
class Mammal{
public:
    Mammal()
    {
        cout << "Constructing Mammal." << endl;
    }
    ~Mammal()
    {
        cout << "Desstructing Mammal." << endl;
    }
};
class Dog :public Mammal {
public:
    Dog()
    {
        cout << "Constructing Dog." << endl;
    }
    ~Dog()
    {
        cout << "Desstructing Dog." << endl;
    }
};
void main()
{
    Dog d;
}

 ⑤组合类的复制构造函数

//类的组合,线段(Line)类
//使用一个类来描述线段,Point类的对象来表示端点,使Line类包括Point类的两个对象p1,p2,作为其数据成员。
//Line类具有计算线段长度的功能,在构造函数中实现
#include<iostream>
#include<cmath>
using namespace std;

class Point {
public:
    Point(int xx = 0, int yy = 0) {//构造函数
        x = xx;
        y = yy;
    }
    Point(Point& p);//复制构造函数
    int getX() { return x; }
    int getY() { return y; }
private:
    int x, y;
};
Point::Point(Point& p) {//复制构造函数的实现
    x = p.x;
    y = p.y;
    cout << "Calling the copy constructor of Point" << endl;
}
//类的组合
class Line {
public:
    Line(Point xp1, Point xp2);
    Line(Line& l);
    double getLen() { return len; }
private:
    Point p1, p2;//Point类的对象p1,p2
    double len;
};
//组合类的构造函数
Line::Line(Point xp1, Point xp2) :p1(xp1), p2(xp2) {
    cout << "Calling constructor of Line" << 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(Line& l) :p1(l.p1), p2(l.p2) {
    cout << "Calling the copy constructor of Line" << endl;
    len = l.len;
}
//主函数
int main()
{
    Point myp1(1, 1), myp2(4, 5);//建立Point类的对象
    Line line(myp1, myp2);//建立Line类的对象
    Line line2(line);//利用复制构造函数建立一个新对象
    cout << "The length of the line is:";
    cout << line.getLen() << endl;
    cout << "The length of the line2 is:";
    cout << line2.getLen() << endl;
    return 0;
}

分析:

  主程序执行时,首先生成连个Point类的对象,然后构造Line类的对象line,接着通过复制构造函数建立Line类的第二个对象line2,最后输出两点的距离。

  整个过程中,Point类的复制构造函数被调用了六次,而且都是在Line类构造函数体运行之前进行的,他们分别是两个对象在Line构造函数进行函数参数形实结合时,初始化内嵌对象时,以及复制构造函数line2时被调用的。两点的距离在Line类的构造函数中求得,存放在其私有数据成员len中,只能通过成员函数getLen()来访问。

  

3.6前向引用声明

Part1.应用场景

C++的类应该先定义再使用,但是在处理相对复杂的问题是,考虑类的组合时,很有可能遇到两个类相互引用的情况,这种情况也称为循环依赖。

因此,无论将哪一个类的定义放在前面,都会引起编译错误。

解决这种问题的方法,就是使用前向引用声明。

Part2.定义及代码

①定义:前向引用声明,是在引用未定义的类之前,将该类的名字告诉编译器,使编译器根据知道那是一个类名。

②例

class B;//前向引用声明
class A{//A类的定义
public:
     void f(B b);//以B类对象b为形参的成员函数
};
class B{//B类的定义
public:
      void g(A a);//以A类对象a为形参的成员函数
};

③注意:前向引用声明不是万能的。

class Frred;//前向引用声明
class Barney{
     Fred x;//错误:类Fred的声明尚不完善,不能定义类Fred的数据成员
};
class Fred{
      Barney y;
}

错误:对类的前向引用声明只能说明Fred是一个类名,而不能给出该类的完整定义,因此在类Barney中就不能定义类Fred的数据成员。

class Frred;//前向引用声明
class Barney{
public:
     ...
     void method(){//错误:Fred类的对象在定义前被使用
          x.yabbaDabbaDo;
          }
private:
      Fred &x;//正确:经过前向引用声明,可以声明Fred类的对象引用或指针
};
class Fred{
public:
      ...
      void yaabaDabbaDo();
private:
      Barney &y;
};

错误:编译时指出错误,因为在类Barney的内联函数中使用了有x所指向的、Fred类的对象,而此时Fred类尚未被完整的定义。

解决方法:更改这两个类的定义次序,或者将函数method()改为非内联形式,并且在类Fred的完整定义之后,再给出函数定义。

原文地址:https://www.cnblogs.com/nanaa/p/11751281.html

时间: 2024-11-10 19:18:05

C++类的组合、前向引用声明的相关文章

C++中对类的提前引用声明注意事项

//或许,友元是VC++6.0心里永远的痛,对于这个BUG我一直很介意.//注:这个程序在VC++6.0里是行不通的,在VS2008里是可以的.#include <iostream> #include <string> using namespace std; class Student; //提前引用声明 //声明Teacher类 class Teacher { public: Teacher(){} Teacher(int i,string s,char c,int t=0):

vs2015如何设置类或函数前不显示引用的数量

这几天,从vs2012换成vs2015,感觉15版本增加了一个类或函数前提示引用的数量,这个感觉很别扭,如何取消显示这个呢? 问题如下: 取消显示这个引用的步骤: 找到菜单栏: 工具 ---> 选项  ---> 文本编辑器 ---> 所有语言 ---> CodeLens 设置取消启用CodeLens,并保存就可以了 设置后结果如下:

类的组合

1.类的组合:类中的成员数据是另一个类的对象.类组合的构造函数:原则:不仅要负责对本类中的基本类型成员数据赋初值,也要对对象成员初始化. 2.构造函数的调用顺序: 1.调用内嵌对象的构造函数(按内嵌时的声明顺序,先声明者先构造): 2.调用本类的构造函数 3.析构函数的调用顺序: 1.调用本类的析构函数: 2.调用内嵌对象的析构函数(按内嵌时的声明顺序,先声明者先析构). 4.若调用默认构造函数(即无形参的),则内嵌对象的初始化也将调用相应的默认构造函数. 5.类声明的方式有两种:使用前向引用声

浅谈类的组合

4. 类的组合 4.1 定义 类中的成员数据是另一个类的对象 4.2 类组合的构造函数设计原则 不仅要负责对本类中基本类型成员数据初始化,也要对对象成员初始化. 4.3 声明形式 类名::类名(对象成员所需的形参,本类成员形参):对象1(参数),对象2(参数),....... {  // 函数体其他语句 } 4.4 例子:类的组合,线段类. #include<iostream> #include<cmath> Using namespace std; Class Point{ Pu

java 报错非法的前向引用

今天在看<thinking in java>的时候,第四章提到了非法的前向引用,于是自己试了一下,书中的例子倒是一下就明白了,但是自己写的一个却怎么也不明白,于是上网问了一位前辈,终于明白啦! 这个是错误的代码: class BB { static int a = 0 ; public BB() { a++ ; System.out.println("执行BB" + a) ; } public void printA() { System.out.println("

【C++基础】类的组合

所谓类的组合是指:类中的成员数据是还有一个类的对象或者是还有一个类的指针或引用.通过类的组合能够在已有的抽象的基础上实现更复杂的抽象. 比如: 1.按值组合 #include<iostream.h> #include<math.h> class Point { public: Point(int xx,int yy)//构造函数 { x=xx; y=yy; cout<<"Point's constructor was called"<<e

iOS开发类别的前向引用

类别(category)除了可以对类进行扩展外,前向引用的能力也很强大.对于一些私有的方法(没有在.h文件里面声明的方法)如果我们想使用的话,有的使用的是performSelector来执行动态调用,这样完全可以调用私用方法,但是这样完全避开了编译器的语法检查,有时候未必是一种好的方法.使用类别来进行前向引用可以解决这个问题. 定义一个item类 @interface ZJItem : NSObject - (void)info; @end .m文件 @implementation ZJItem

Javac编译时报“非法的前向引用”,以及Eclipse自带的Java编译器的缺陷

编译以下类的时候,如果切换WARNING_LEVEL的赋值方式为"= StatusDef.WARNING",javac会报"非法的前向引用". Eclipse自带的编译器会编译通过,这是Eclipse编译器的一个缺陷. 产生这个编译错误的原因是,cinit方法是按照字段在源码中的声明顺序依次执行各个初始化代码段. 对于ForwardReference类,cinit方法依次执行以下功能: 通过NORMAL_LEVEL的数值,创建C0000_1字段. 在ForwardR

计算机程序的思维逻辑 (14) - 类的组合【转】

正所谓,道生一,一生二,二生三,三生万物,如果将二进制表示和运算看做一,将基本数据类型看做二,基本数据类型形成的类看做三,那么,类的组合以及下节介绍的继承则使得三生万物. 上节我们通过类Point介绍了类的一些基本概念和语法,类Point中只有基本数据类型,但类中的成员变量的类型也可以是别的类,通过类的组合可以表达更为复杂的概念. 程序是用来解决现实问题的,将现实中的概念映射为程序中的概念,是初学编程过程中的一步跨越.本节通过一些例子来演示,如何将一些现实概念和问题,通过类以及类的组合来表示和处