zoukankan      html  css  js  c++  java
  • 定义抽象数据类型

    在C++语言中,我们使用类定义自己的数据类型。通过定义新的类型来反映待解决问题中的各种概念,可以使我们更容易编写,调试,和修改程序。

    类的基本思想是数据抽象和封装。数据抽象是一种依赖于接口和实现分离的编程技术。累得接口包括用户所能执行的操作;类的实现则包括类的数据成员、负责接口实现的函数体以及类所需的各种私有函数。

      封装实现了类的接口和实现的分离。封装后的类隐藏了他的实现细节,也就是说,类的用户只能使用接口而无法访问实现的部分。

      类要想实现数据抽象个封装,需要首先定义一个抽象数据类型。在抽象数据类型中,由类的设计者负责考虑类的实现过程,使用该类的程序员则只需要抽象的思考类型做了什么,而无需了解类型的工作细节。

    1、定义抽象数据类型:

    (1)设计sales_data类:

    我们的最终目的是令sales_data支持与sales_items类完全一样的操作集合。sales_items有一个名为isbn的成员函数,并且支持+-×/等运算符。

    注意:我们将在14章学习如何自定义运算符。现在我们先为这些运算定义普通函数形式。

    综上,sales_data的接口因该包含以下操作:

    『』一个isbn成员函数,用于返回isbn编号

    『』一个combine成员函数,用于将一个sales_data对象加到另一个对象上。

    『』一个名为add的函数,执行两个sales_data对象的加法

    『』一个read函数,将数据从istream读入到sales_data对象中

    『』一个print函数,将sales_data对象的值输出到ostream

    关键概念:不同的编程角色

    程序员把运行程序的人称作用户(user)。类似的,类的设计者也是为其用户设计并实现一个类的人:显然类的用户是程序员,而非应用程序的最终使用者。

    使用改进sales_data类:

    在考虑如何实现类之前,首先来看看如何使用上面这些接口函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    Sales_data total;//保存当前求和结果的变量
    if(read(cin,total))//读入第一笔交易
    {
        Sales_data trans;//保存下一条交易数据的变量
        while(read(cin,trans))//读入剩余交易
        {
            if(total.isbn()==trans.isbn())//检查isbn
                total.combine(trans);//更新变量total当前的值
            else
            {
                print(cout,total)<<endl;//输出结果
                total=trans;//处理下一本书
            }
        }
        print(cout,total)<<endl;//输出最后一条交易
    }
    else//没有输入任何信息
    {
        cerr<<"no data?!"<<endl;//通知用户
    }
    1. #include <iostream>  

    2. #include<string>  

    3. #include<vector>  

    4. using namespace std;  

    5. /* 

    6. C++ 中保留了C语言的 struct 关键字,并且加以扩充。在C语言中,struct 只能包含成员变量,不能包含成员函数。 

    7. 而在C++中,struct 类似于 class,既可以包含成员变量,又可以包含成员函数。 

    8.  

    9. C++中的 struct 和 class 基本是通用的,唯有几个细节不同: 

    10. 1:使用 class 时,类中的成员默认都是 private 属性的;而使用 struct 时,结构体中的成员默认都是 public 属性的。(最本质的区别) 

    11. 2:class 继承默认是 private 继承,而 struct 继承默认是 public 继承(《C++继承与派生》一章会讲解继承)。 

    12. 3:class 可以使用模板,而 struct 不能。 

    13. */  

    14.   

    15. //利用class定义类  

    16. class Student  

    17. {  

    18.     Student(string name, int age, int score);//构造函数  

    19.   

    20.     string m_name;  

    21.     int m_age;  

    22.     int m_score;//定义三个变量  

    23.   

    24.     void showname()//定义一个函数  

    25.     {  

    26.         cout<<m_name<<"的年龄是:"<<m_age<<",得分为:"<<m_score<<endl;  

    27.     }//类内定义的函数,编译器会优先视为内联函数  

    28. public:  

    29. private:  

    30. protected://三种形式  

    31. };  

    32. Student::Student(string name, int age, int score):m_name(name), m_age(age), m_score(score){ }  

    33. //成员初始化列表,将name赋给m_name,改变类内变量  

    34.   

    35.   

    36. //利用strcut定义类  

    37. struct Students  

    38. {  

    39.     Students(string name, int age, int score);//构造函数  

    40.       

    41.     string m_name;  

    42.     int m_age;  

    43.     int m_score;//定义三个变量,默认public  

    44.   

    45.     void shownames()//定义一个函数  

    46.     {  

    47.         cout<<m_name<<"的年龄是:"<<m_age<<",得分为:"<<m_score<<endl;  

    48.     }//类内类外定义皆是在同文件中,也可定义在.h文件中。  

    49. };  

    50. Students::Students(string name, int age, int score):m_name(name), m_age(age), m_score(score){ }  

    51. //列表初始化,覆盖类的内部变量  

    52.   

    53. /* 

    54. 总结 

    55. 1:struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的 

    56. 2:当你觉得你要做的更像是一种数据结构的话,那么用struct,如果你要做的更像是一种对象的话,那么用class。  

    57. 3:然而对于访问控制,应该在程序里明确的指出,而不是依靠默认,这是一个良好的习惯,也让你的代码更具可读性。  

    58. */  

    59.   

    60. int main(int argc, char** argv)  

    61. {  

    62.     Student stu1("小明", 18, 3);//报错:虽然已声明,但是不可访问。成员没有限定public,默认都是private,外部不可访问。  

    63.     Students stu2("小明", 18, 3);//正常  

    64.     stu2.shownames();  

    65.     return 0;  

    66. }  

    2、定义改进的sales_data类:

    改进之后的类的数据成员将与之前保持一致。

    类将包含两个成员函数:combine和isbn。此外,我们还将赋予sales_data另外一个成员函数用于返回售出书记的平均价格,这个函数被命名为avg_price。因为avg_price的目的并非通用,所以它应该属于类的实现的一部分,而非接口的一部分。

    成员函数的声明必须在类的内部,它的定义则能在类的内部也可以在外部。作为接口组成部分的非成员函数,例如:add,read,print等,他们的定义和声明都在类的外部:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct Sales_data
    {
            std::string isbn()const{return bookno;}
            Sales_data & combine(const Sales_data&);
            double avg_price() const;
            std::string bookno;
            unsigned units_sold=0;
            double revenue =0.0;
    };
    1
    2
    3
    Sales_data add(const Sales_data &, const Sales_data &);
    std::ostream &print(std::ostream&, const Sales_data &);
    std::istream &read(std::istream&, Sales_data &);

    注意定义在类内部的函数是隐式的inline函数。

    首先介绍,isbn函数,它的参数列表为空,返回值是一个string对象,成员函数体也是一个块。在isbn中快只有一条return语句,用于返回sales_data对象的bookno数据成员。关于isbn函数一件有意思的事情是:它是如何获得bookno数据成员的那。

    在成员内部,我们可以直接使用调用该函数的对象的成员,而无需通过成员访问运算符来做到这一点,因为this所指的正是这个对象。任何对类的直接访问都被看作this的隐式引用,也就是当isbn使用bookno时,他隐式的使用this指向的成员,就像我们书写了this->bookno一样。

    引入const成员函数:

    isbn函数的另外一个关键之处是紧随参数列表之后的const关键字,这里,const的作用是修改隐式this指针类型。

    默认情况下,this的类型是指向类类型非常量版本的常量指针。例如在sales_data成员函数中,this的类型是sales_data *const。尽管this是隐式的,但它仍然需要遵循初始化规则,意味着我们不能把this绑定到一个常量对象上。这一情况也就使我们不能吧this绑定到一个常量对象上。使我们不能在一个常量对象上调用普通的成员函数。

    在C++中,把const关键字放在成员函数的参数列表之后,此时,紧跟在参数列表后面的const表示this是一个指向常量的指针。像这样使用const的成员函数被称作常量成员函数。

    常量对象,以及常量对象的引用或指针都只能调用常量成员函数。

    类作用域和成员函数:

    类本身就是一个作用域,类的成员函数的定义嵌套在类的作用域之内,因此,isbn中用到的名字bookno其实就是定义在sales_data内的数据成员。

    在类的外部定义成员函数:

    像其他函数一样,在类的外部定义成员函数时,成员函数的定义必须与他的生命匹配。也就是说,返回类型,参数列表,函数名都与类内部的声明保持一致。如果成员被声明称常量成员函数,那么他的定义也必须在参数列表后明确的制定const属性。同时,类外部定义的成员的名字必须包含它所属的类名。

     

    作用域运算符::一旦编译器看到这个函数名,就能理解剩余的代码是位于类的作用域内的。

  • 相关阅读:
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    Python for Data Science
    软件工程实践总结
  • 原文地址:https://www.cnblogs.com/yjds/p/8597247.html
Copyright © 2011-2022 走看看