《C++Primer》读书笔记(七)类

类的基本思想是数据抽象封装

数据抽象是一种依赖于接口和实现分离的编程技术;类的接口包括用户所能执行的操作,类的实现包括类的数据成员、负责接口实现的函数体以及定义类所需的各种是有函数

定义抽象数据类型

设计Sales_data类

Sales——data的接口应该包含以下功能
- 一个isbn成员函数,用于返回对应的isbd编号
- 一个combine成员函数,用于将一个Sales_data对象加到另一个对象上
- 一个名为add的函数,执行两个Sales_data对象的加法
- 一个read函数,将数据从istream读入到Sales_data对象中
- 一个print函数,将Sales_data对象的值输出到ostream

(1)使用改进的Sales_data类
- 首先,我们看看应该如何使用上面的这些接口函数

Sales_data total;
if(read(cin,total)){
    Sales_data trans;
    while(read(cin,trans)){
        if(total.isbn()==trans.isbn())
            total.combine(trans);
        else{
            print(cout,total)<<endl;
            total=trans;
        }
    }
    print(cout,total)<<endl;
}
else{
    cerr<<"No data?!"<<endl;
}

定义改进的Sales_data类

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;
};
Sales_data add(const Sales_data&,const Sales_data&);
std::ostream &print(std::ostream&,const Sales_data&);
std::istream &read(std::isteam&,Sales_data&);

(1)定义成员函数

(2)引入this
- 实际上,调用一个成员函数,是在为某个对象调用它。
- 在成员函数 内部,任何类成员的访问都被看作this的隐式引用,直接使用bookNo,形同于this->bookNo

(3)引入const成员函数
- const的作用是修改隐式this指针的类型
- C++的做法是允许把const关键字放在成员函数的参数列表之后,此时,紧跟在参数列表后面的const表示this是一个指向常量的指针。像这样使用const的成员函数被称作常量成员函数

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

(4)类作用域和成员函数

(5)在类的外部定义成员函数

double Sales_data::avg_price() const{
    if(units_sold)
        return revenue/units_dold;
    else
        return 0;
}
  • 一旦编译器看到改函数名,就知道其剩余代码是位于类的作用域内的

(6)定义一个返回this对象的函数
- 函数combine的设计初衷类似于复合赋值+=,调用改函数的对象代表赋值运算符左侧对象,右侧对象则通过显式的实参被传入函数

Sales_data& Sales_data::combine(const Sales_data &rhs){
    units_sold+=rhs.units_sold;
    revenue+=rhs.revenue;
    return *this;
}

当我们的交易处理程序调用如下的函数时,

total.combine(trans);

total的地址被绑定到隐式的this参数上,而rhs绑定到了trans上

定义类相关的非成员函数

一般来说,如果非成员函数是类接口的组成部分,则这些函数的声明应该与类在同一个头文件内

(1)定义read和print函数

istream &read(istream &is,Sales_data &item){
    double price=0;
    is>>item.bookNo>>item.units_sold>>price;
    item..revenue=price*item.units_sold;
    return is;
}
ostream &print(ostream &os,const Sales_data &item){
    os<<item.isbn()<<" "<<item.units_sold<<" "
        <<item.revenue<<" "<item.avg_price();
    return os;
}
  • 使用IO类引用是因为其IO类属于不能被拷贝的类型,因此我们只能通过引用来传递它们

(2)定义add函数

Sales_data add(const Sales_data &lhs,const Sales_data &rhs){
    Sales_data sum=lhs;
    sum.combine(rhs);
    return sum;
}

构造函数

(1)合成的默认构造函数

(2)某些类不呢依赖于合成的默认函数

(3)定义Sales_data的构造函数

struct Sales_data{

    Sales_data()=default;
    Sales_data(const std::String &s):bookNo(s){}
    Sales_data(const std::string &s,unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n){}
    Sales_data(std::istream &);

    std::string isbn() const{ retrun bookNo;}
    Sales_data& combine(const Sales_data&);
    double avg_price() const;
    std::string bookNo;

    unsigned units_sold=0;
    double revenue=0.0;
};

(4)=default的含义

(5)在类的外部定义构造函数

Sales_data::Sale_data(std::istream &is){
    read(is,*this);
}

拷贝、赋值和折构

访问控制与封装

class Sales_data{
public:
    Saels_data()=default;
    Sales_data(const std::string &s.unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n) { }
    Sales_data(const std::stirng &s):bookNo(s) { }
    std::string isbn() const { return bookNo; 
    Sales_data &combine(const Sales_data&);
private:
    double avg_price() const{
        return units_sold?revenue/units_sold:0;
    };

    std:string bookNo;
    unsigned units_sold=0;
    double revenue=0.0;
};

(1)使用class或struct关键字
- class默认权限为private
- struct默认权限为public

友元

  • 类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为它的友元
class Sales_data{
    friend Sales_data add(const Sales_data&,const Sales_data&);
    friend std::istream &read(std::istream&,Sales_data&);
    friend std:ostream &print(std::ostream&,const Sales_data&);

public:
    Sales_data()=default;
    Sales_data(const std::string &s.unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n) { }
    Sales_data(std::istream&);
    Sales_data(const std::stirng &s):bookNo(s) { }
    std::string isbn() const { return bookNo; 
    Sales_data &combine(const Sales_data&);
private:
    std::string bookNo;
    unsigned units_sold=0;
    double revenue=0.0;
};
Sales_data add(const Sales_data&,const Sales_data&);
std::istream &read(std::istream&,Sales_data&);
std::osteam &print(std::ostream& const Sales_data&);

类的其他特性

类成员再探

(1)定义一个类型成员

(2)Screen类的成员函数

class Screen{
public:
    typedef std::string::size_tyoe pos;
    Screen()=default;

    Screen(pos ht,pos wd,char c):height(ht),width(wd),contents(ht * wd,c){ }
    char get()const{
        return contents[cursor];
    }
    inline char get(pos ht,pos wd) const;
    Screen &move(pos r,pos c);
private:
    pos cursor=0;
    pos height=0,width=0;
    std::string contents;
};

(3)令成员你作为内联函数

inline
Screen &Screen::move(pos r,pos c){
    pos row=r*width;
    cursor=row+c;
    return *this;
}
char Screen::get(pos r,pos c) const{
    pos row=r*width;
    return contents[row+c];
}

(4)重载成员函数

(5)可变数据成员
- 一个可变数据成员永远不会是const,即使它是const对象的成员
- 小例子:我们将给Screen添加一个名为access_ctr的可变成员,通过它我们可以追踪每个Screen的成员函数被调用了多少次

class Screen{
public:
    void some_member() const;
private:
    mutable size_t access_ctr;
};
void Screen::some_memeber() const{
    ++access_ctr
}

(6)类数据成员的初始值

class Window_mgr{
    private:
    std::vector<Screen> screens{Screen(24,80,' ')};
}

当我们提供一个类内初始值时,必须比符号=或者花括号表示

返回*this的成员函数

class Screen {
public:
    Screen &set(char);
    Screen &set(pos,pos,char);
};
inline Screen &Screen::set(char c){
    contents[cursor]=c;
    return *this;
}
inline Screen &Screen::set(pos r,pos col,char ch){
    contents[r*width+col]=ch;
    return *this;
}
  • set成员返回值时调用set的对象的引用
  • 返回引用的函数是左值的,意味着这些函数返回的是对象本身而非副本

把一系列这样的操作链接在一起:

myScreen.move(4,0).set('#');
//这些操作将在同一个对象上执行`
  • 如果我们令move和set返回Screen而非Screen&,则上述语句的行为将大不相同(相当于对返回值进行拷贝,然后不改变myScreen)

(1)从const成员函数返回*this

一个const成员函数如果以应用的形式返回*this,那么他的返回类型将是一个常量引用

Screen mySCreen;
// 如果display返回常量引用,则调用set将引发错误
myScreen.display(cout).set('*');

(2)基于const的重载

建议:对于公共代码使用私有功能函数

类类型

  • 每个类定义了唯一的类型。对于两个类来说,即使它们的成员完全一样,这两个类也是两个不同的类型

(1)类的声明

友元再探

(1)类之间的友元关系

class Screen{
    friend class Window_mgr;
}
class Window_mgr{
public:
    using ScreenIndex=std::vector<screen>::size_tyoe;
    void clear(ScreenIndex);
private:
    std::vector<Screen> screens{Screen(24,80,'')};
};
void Window_mgr::clear(ScreenIndex i){
    Screen &s=screens[i];
    s.centents=string(s.height * s.width,' ');
}

(2)令成员函数作为友元

class Screen{
    friend void Window_mrg::clear(ScreenIndex);
}

(3)函数重载和友元

(4)友元声明和作用域

类的作用域

(1)作用域和定义在类外部的成员

发布了34 篇原创文章 · 获赞 5 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章