zoukankan      html  css  js  c++  java
  • 2-类与对象1.4

    组合

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

    实际上类的成员数据既可以是基本类型也可以是自定义类型,当然也可以是类的对象。

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

    当创建类的对象时,如果这个类具有内嵌对象成员,那么各个内嵌对象将首先被自动创建。因此,在创建对象时既要对本类的基本类型数据成员进行初始化,又要对内嵌对象成员进行初始化。这时,理解这些对象的构造函数被调用的顺序就很重要了。

    组合类构造函数定义的一般形式为:

    类名::类名(形参表):内嵌对象1(形参表),内嵌对象2(形参表),...

    {类的初始化}

    其中,内嵌对象1(形参表),内嵌对象2(形参表),...称作初始化列表,其作用是对内嵌对象进行初始化。

    对基本类型的数据成员也可以这样初始化,而且使用初始化列表比使用赋值语句的效率要高。如

    Circle::Circle(float r):radius(r){}

    在创建一个组合类的对象时,不仅它自身的构造函数将被调用,而且还将调用内嵌对象的构造函数。这时构造函数的调用顺序是:

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

    b、执行本类构造函数的函数体。

    如果声明组合类的对象时没有指定对象的初始值,则默认形式(无形参)的构造函数被调用,这时内嵌对象的默认形式构造函数也被调用。析构函数的调用执行顺序与构造函数刚好相反。

    对于一个类,如果没有编写拷贝拷贝构造函数,编译系统会在必要时自动生成一个默认的拷贝构造函数。若建立组合类的对象时调用默认拷贝构造函数,则编译器将自动调用内嵌成员对象的拷贝构造函数。

    如果要为组合类编写拷贝构造函数,则需要为内嵌成员对象的拷贝构造函数传递参数,如假设C类中包含B类的对象b作为成员,C类的拷贝构造函数形式如下:

    C::C(C &c1):b(c1.b)

    {...}

    例子:

    类的组合,线段类,由两个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<<"Point 拷贝构造函数被调用"<<endl;
    }
    class Line
    {
    public:
    Line(Point xp1,Point xp2);
    Line(Line &);
    double GetLen(){return len;}
    private:
    Point p1,p2;
    double len;
    };
    Line::Line(Point xp1,Point xp2):p1(xp1),p2(xp2)
    {
    cout<<"Line 构造函数被调用"<<endl;
    double x=double(p1.GetX()-p2.GetX());
    double y=double(p1.GetY()-p2.GetY());
    len=sqrt(x*x+y*y);
    }
    Line::Line(Line &L):p1(L.p1),p2(L.p2)
    {
    cout<<"Line拷贝构造函数被调用"<<endl;
    len = L.len;
    }

    int main()
    {
    Point myp1(1,1),myp2(4,5);
    Line line(myp1,myp2);
    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;
    }

    2、前向引用声明

    考虑到类的组合中,很可能遇到两个类相互引用的情况,这种情况也称为循环依赖,因为不管把谁放在前面都会存在未定义先使用的情况,故引入了前向引用,它是在引用未定义的类之前,将该类的名字告诉编译器,使编译器知道那是一个类名。这样,当程序中使用这个类名时,编译器就不会认为是错误,而类的完整定义可以在程序的其他地方。

    class B;//前向引用声明

    class A

    {

      public:

        void f(B b);//以B类对象b为形参的成员函数

    };

    class B

    {

      public:

        void g(A a);//以A类对象a为形参的成员函数

    }

    应该记住,当使用前向引用声明时,只能使用被声明的符号,而不能涉及类的任何细节

  • 相关阅读:
    leetcode: power of three 三种解法
    继续写java和socket
    node中的事件发射器
    谈一谈Crsf和XSS攻击
    谈一谈那些框架们
    【Mysql数据库】学习笔记
    【数据库】DML-增删改查-SQL实现
    【Jsp,Servlet初学总结】 含 cookie和session的使用
    struct和typedef struct彻底明白了
    Android 学习之路
  • 原文地址:https://www.cnblogs.com/gary-guo/p/6213068.html
Copyright © 2011-2022 走看看