C++ Primer 学习笔记_19_类与数据抽象(1)_类的定义和声明

C++ Primer 学习笔记_19_类与数据抽象(1)_类的定义和声明

在C++中,用类来定义自己的抽象数据类型。通过定义类型来对应所要解决的问题中的各种概念,可以使我们更容易编写、调试和修改程序。可以使得自己定义的数据类型用起来与内置类型一样容易和直观。

看一下Sales_item类:

class Sales_item
{
private:
    std::string isbn;
    unsigned units_sold;
    double revenue;
 
public:
    double ave_price() const;
    bool same_isbn(constSales_item &rhs) const
    {
        return isbn ==rhs.isbn;
    }
 
   Sales_item():units_sold(0),revenue(0) {}
};
 
double Sales_item::ave_price() const
{
    if (!units_sold)
    {
        return 0;
    }
    return revenue /units_sold;
}

一、类定义:扼要重述

简单来说,类就是定义了一个新的类型和一个新的作用域。

 

1、类成员

一个类可以包含若干公有的、私有的和受保护的部分。我们已经使用过public 和private访问标号:在public部分定义的成员可被使用该类型的所有代码访问:在private部分定义的成员可被其他类成员访问。

所有成员必须在类的内部声明,一旦类定义完成之后,就没有任何方式可以增加成员了。

 

2、构造函数

在创建一个类对象时,编译器会自动使用一个构造函数来初始化该对象,也就是说:构造函数用于给每个数据成员设定适当的初始值。

构造函数一般使用一个构造函数初始化列表,来初始化对象的数据成员。构造函数初始化类表由成员名和带括号的初始值组成,跟在构造函数的形参表之后,并以冒号(:)开头。

比如:Sales_item():units_sold(0),revenue(0){}

 

3、成员函数

在类内部定义的函数默认为内联函数(inline)。

而在类的外部定义的函数必须指明它们是在类的作用域中。

就const关键字加在形参表之后,就可以将成员函数声明为常量,如:double avg_price() const;

const必须同时出现在声明和定义中,若只出现在一处,则会出现编译时错误!

//类内
double avg_price() const;
//类外
double Sales_item::ave_price()  //error
{
    if (!units_sold)
    {
        return 0;
    }
    return revenue /units_sold;
}

二、数据抽象和封装

数据抽象是一种依赖于接口和实现分离的编程技术:类的设计者必须关心类是如何实现的,但使用该类的程序员不必了解这些细节

封装是一项将低层次的元素组合起来形成新的、高层次实体的技术!

1、访问标号实施抽象和封装

1)程序的所有部分都可以访问带有public标号的成员。类型的数据抽象视图由其public成员定义。

2)使用类的代码不可以访问带有private标号的成员。private封装了类型的实现细节。

 

2、编程角色的不同类别

好的类的设计者会定义直观和易用的类接口,而用户(此处可以指程序员)则只需关心类中影响他们使用的那部分实现。

注意,C++程序员经常会将应用程序的用户和类的使用者都称为“用户”。

 

【关键概念:数据抽象和封装的好处】

1)避免类内部出现无意的、可能破坏对象状态的用户级错误。

2)随着时间的推移,可以根据需求改变或缺陷报告来完善类实现,而无需改变用户级代码。

【注解】

改变头文件中的类定义可有效地改变包含该头文件的每个源文件的程序文本,所以,当类发生改变时,使用该类的代码必须重新编译

 

三、关于类定义的更多内容

1、同一类型的多个数据成员

普通的声明:

class Screen
{
public:
    //...
 
private:
    std::string contents;
    std::string::size_type cursor;
    std::string::size_typeheight,width;
};


2、使用类型别名来简化类

出了定义数据和函数成员之外,类还可以定义自己的局部类型名字。如果为std::string::size_type提供一个类型别名,那么Screen类将是一个更好的抽象(将index定义放在public部分):

class Screen
{
public:
   //...
   typedef std::string::size_type index;
 
private:
   std::string contents;
   index cursor;
   index height,width;
};



四、类声明和类定义

在一个给定的源文件中,一个类只能被定义一次。如果在多个文件中定义一个类,那么每个文件中的定义必须是完全相同的

可以声明一个类而不定义它:

//前向声明
class Screen;

在声明之后、定义之前,类Screen是一个不完全类型,即:已知Screen是一个类型,但不知道包含哪些成员。

不完全类型(incompletetype)只能以有限方式使用不能定义该类型的对象。不完全类型只能用于定义指向该类型的指针及引用,或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数。

在创建类的对象之前,必须完整的定义该类。必须是定义类,而不是声明类,这样,编译器就会给类的对象预定相应的存储空间。同样的,在使用引用或指针访问类的成员之前,必须已经定义类

 

1、一般类声明

//类是一种用户自定义类型,声明形式:
class 类名称
{
   public:
             公有成员(外部接口)
   private:
             私有成员
   protected:
             保护成员
};

2、示例
//Clock.h
//#pragma once
#ifndef _CLOCK_H_
#define _CLOCK_H_
class Clock
{
public:
 void Display();
 void Init(int hour, int minute, int second);
 void Update();
 int GetHour();
 int GetMinute();
 int GetSecond();
 void SetHour(int hour);
 void SetMinute(int minute);
 void SetSecond(int second);
private:
 int hour_;
 int minute_;
 int second_;
};
#endif // _CLOCK_H_
//Clock.c
#include "Clock.h"
#include <iostream>
using namespace std;
void Clock::Display()
{
 cout<<hour_<<":"<<minute_<<":"<<second_<<endl;
}
void Clock::Init(int hour, int minute, int second)
{
 hour_ = hour;
 minute_ = minute;
 second_ = second;
}
void Clock::Update()
{
 second_++;
 if (second_ == 60)
 {
  minute_++;
  second_ = 0;
 }
 if (minute_ == 60)
 {
  hour_++;
  minute_ = 0;
 }
 if (hour_ == 24)
 {
  hour_ = 0;
 }
}
int Clock::GetHour()
{
 return hour_;
}
int Clock::GetMinute()
{
 return minute_;
}
int Clock::GetSecond()
{
 return second_;
}
void Clock::SetHour(int hour)
{
 hour_ = hour;
}
void Clock::SetMinute(int minute)
{
 minute_ = minute;
}
void Clock::SetSecond(int second)
{
 second_ = second;
}
//main.c
#include "Clock.h"
int main(void)
{
 Clock c;
 c.Init(10, 10, 10);
 c.Display();
 //c.second_ += 1;
 c.Update();
 c.Display();
 //c.hour_ = 11;
 c.SetHour(11);
 c.Display();
 return 0;
}
运行结果:
10:10:10
10:10:11
11:10:11

3、公有、私有、保护成员

(1)在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。

(2)在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。
(3)在关键字protected后面声明,与private类似,其差别表现在继承与派生时对派生类的影响不同

4、数据抽象和封装

(1)数据抽象是一种依赖于接口和实现分离的编程(和设计)技术。类设计者必须关心类是如何实现的,但使用该类的程序员不必了解这些细节。使用者只要抽象地考虑该类型做什么,而不必具体地考虑该类如何工作。
(2)封装是一项将低层次的元素组合起来形成新的、高层次的实体的技术。函数是封装的一种形式:函数所执行的细节行为被封装在函数这个更大的实体中。被封装的元素隐藏了它们的实现细节——可以调用函数,但是不能直接访问函数所执行的语句。同样地,类也是一个封装的实体:它代表若干成员的聚集,设计良好的类隐藏了类实现的细节。


5、为类的成员使用类声明:

    只有当类定义已经在前面出现过,数据成员才能被指定为该类类型。如果该类型是不完全类型,那么数据成员只能是指向该类类型的指针引用

    因为只有当类定义体完成后才能定义类,因此类不能具有自身类型的数据成员。然而,只要类名一出现就可以认为该类已声明。因此,类的数据成员可以是指向自身类型的指针或引用:

class LinkScreen

{

   Screen Window;

   LinkScreen *next;

   LinkScreen *prev;

};


 

五、类对象

定义一个类时,也就是定义了一个类型。一旦定义了类,就可以定义该类型的对象。定义对象时,将为其分配内存空间,但(一般而言)定义类型时不进行存储分配。一旦定义了对象,编译器则为其分配足够容纳一个类对象的存储空间

每个对象具有自己的类数据成员的副本。修改其中一个对象不会改变其他该类对象的数据成员。

 

1、定义类类型的对象

定义了一个类类型之后,可以按以下两种方式使用。

1)将类的名字直接用作类型名。

2)指定关键字class或struct,后面跟着类的名字:

   Screen scr;
   //两条语句作用相同
   class Screen scr;

2、为什么类的定义以分号结束

因为在类定义之后可以接一个对象定义列表,所以,定义必须以分号结束,可以给一个《实践:求主元素》

//vs2012测试代码
#include<iostream>
#include<vector>
 
usingnamespace std;
 
#definen 5
 
//方法一:每找出两个不同的element,就成对删除即count--,最终剩下的一定就是所求的。时间复杂度:O(n)
classSolution {
public:
    int majorityElement(vector<int>&num) {
              int element;
        int length = num.size();
              int count = 0;
              for(int i = 0; i < length; i++)
              {
                     if(count == 0)
                     {
                            element = num[i];
                            count = 1;
                     }
                     else
                     {
                            if(num[i] ==element)
                                   count++;
                            else
                                   count--;
                     }
              }
              count = 0;
              for(int i = 0; i < length; i++)
              {
            if(num[i] == element)
                count++;
              }
 
        if(count > length / 2)
            cout << element <<endl;
        else
            cout << "can not findthe majority element" << endl;
 
              return 0;
    }
}lin, lin2;
 
int main()
{
       int a;
       vector<int> num;
       for(int i=0; i<n; i++)
       {
              cin>>a;
              num.push_back(a);
       }
       lin.majorityElement(num);
}

运行结果:

输入:23 2 3 2

2

 

【注解】

通常,将对象定义成类定义的一部分是个坏主意!!!这样做,会使所发生的操作难以理解。对读者而言,将两个不同的实体(类和变量)组合在一个语句中,也会令人迷惑不解。

 

你可能感兴趣的:(C++,C++,STL,Primer,标准模板库)