《C++Primer 5e》学习笔记(6):类

#include <iostream>
using namespace std;


struct Sales_data
{
    friend Sales_data add(const Sales_data&,const Sales_data&);
    friend istream &read(istream&,Sales_data&);
    friend ostream &print(ostream&,const Sales_data&);
public:

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

    string isbn() const {return bookNo;}
    Sales_data& combine(const Sales_data&);
private:

    double avg_price() const ;
    string bookNo; //表示ISBN号
    unsigned units_sold=0; //某本书的销量
    double revenue=0.0; //这本书的总销售收入
};

Sales_data add(const Sales_data&,const Sales_data&);
ostream &print(ostream &,const Sales_data&);
istream &read(istream&,Sales_data&);

Sales_data::Sales_data(istream &is)
{
    read(is,*this); //从is中读取一条信息然后存入this中
}

double Sales_data::avg_price() const
{
    if(units_sold) return revenue/units_sold;
    else return 0;
}

Sales_data& Sales_data::combine(const Sales_data &rhs)
{
    units_sold+=rhs.units_sold; //把rhs的成员加到this对象的成员上
    revenue+=rhs.revenue;
    return *this; //返回调用该函数的对象
}

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();
}

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

int main()
{
    Sales_data total; //保存当前求和结果的变量
    Sales_data trans; //保存下一条交易数据的变量
    total.combine(trans); //返回total的引用

    return 0;
}






1.定义在类内部的函数是隐式的inline函数

2.成员函数通过this隐式参数来访问调用它的那个对象。当我们调用一个成员函数时,用请求该函数的对象的地址去初始化this。

3.this的目的是总指向“这个”对象,所以默认情况下this的类型是指向类类型非常量版本的常量指针,我们不允许改变this中保存的地址。这也意味着this不能绑定到一个常量对象上,从而导致我们不能在一个常量对象上调用普通的成员函数。C++允许我们把const关键字放在成员函数的参数列表后,以紧跟在参数列表之后的const来表示this指针是一个指向常量的指针:

std::string isbn() const 
{
    return bookNo;//等价于return this->bookNo
}


4.编译器分两步处理类:首先编译成员的声明,然后才轮到成员函数体。因此,成员海曙可以随意使用类中的其他成员而无须在意这些成员出现的次序。

5.类外部定义的成员必须包含他所属的类名:

double Sales_data::avg_price const (){ /*..*/} //Sales_date为类名


6.

Sales_data& Sales_data::combine(const Sales_data &rhs)
{
    units_sold+=rhs.units_sold; //把rhs的成员加到this对象的成员上
    revenue+=rhs.revenue;
    return *this; //返回调用该函数的对象
}

Sales_data total,trans;
total.combine(trans); //返回total的引用


我们无须使用隐式的this指针访问函数调用者的某个具体成员,而是需要把调用函数的对象当成一个整体来访问;return语句解引用this指针以获得执行该函数的对象。PS:返回引用的函数是左值,意味着这些函数返回的是对象本身而不是对象的副本。

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

7.构造函数的任务是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。不同于其他函数,构造函数不能被声明成const的。当我们创建类的一个const对象时,直到构造函数完成初始化过程,对象才能真正取得其“常量”属性。因此,构造函数在const对象的构造过程中可以向其写值。

8.合成的默认构造函数(由编译器自动生成的的)的初始化规则:如果雷内存在初始值,用它来初始化成员;否则,默认初始化。PS:编译器只有在发现类不包含任何构造函数的情况下才会替我们生成一个默认的构造函数。

9.每个访问说明符制定了接下来的成员的访问级别,其有效范围直到出现下一个访问说明符或者到达类的结尾处为止。

10.关键字class关键字struct定义类唯一的区别就是默认的访问权限不同:class则定义在第一个访问说明符之前的成员是private的;而用struct的话,这些成员是public的。

11.友元声明只能出现在类定义的内部,但是在类内出现的具体位置不限。友元不是类的成员函数,也不受它所在区域访问控制级别的约束。注意:友元关系不存在传递性。

如果一个类想要把一组重载函数声明为它的友元函数,它需要对这组重载函数中的每一个分别声明。

12.可变数据成员(mutable)永远不会是const,即使它是const对象的成员。因此,一个const成员函数可以改变一个可变成员的值。

13.我们可以只声明一个类而不去定义它:

class Sales_data; //Sales_data类的声明


但在我们创建一个类的对象之前该类必须被定义过,类似的,类也必须首先被定义,然后才能用引用或指针访问其成员。即:只声明不定义类,可以定义指向它的引用或指针,但不能用引用或指针访问类。

14.编译器处理完类中全部的声明之后才会处理成员函数的定义。

15.如果成员是const、引用、或者属于某种未提供默认构造函数的类类型,我们必须通过构造函数初始值列表为这些成员提供初始值。PS:我们初始化const或者引用类型的数据成员的唯一机会就是通过构造函数初始值。

16.构造函数初始值列表只是说明了用于初始化成员的值,而不限定初始化的具体执行顺序。

17.如果一个构造函数为所有参数都提供了默认实参,则它实际上也定义了默认构造函数。

18.能通过一个实参调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的规则。PS:该规则只允许一步类类型转换。

string null_book="9-999-9999-9";
//构造一个临时的Sales_data对象
//该对象的units_sold和revenue等于0,bookNo等于null_book
item.combine(null_book); 

//错误:需要用户定义两步转换:
//(1).把"9-999-9999-9"转换成string
//(2)再把这个临时的string转换成Sales_data
item.combine("9-999-9999-9");


19.通过将构造函数声明为explicit可以阻止类类型的隐式转换:只能在类内声明构造函数时使用关键字explicit,在类外部定义时不应重复。explicit只对一个实参的构造函数有效(因为隐式转换只能通过有一个实参的构造函数调用)。PS:explicit构造函数只能用于直接初始化,而不能用于赋值初始化:

class Sales_data
{
    explicit Sales_data(const string &s):bookNo(s){}
};

Sales_data item1(null_book); //正确:直接初始化
Sales_data item2=null_book //错误:不能将explicit构造函数用于拷贝形式的初始化


20.通过关键字static可以将成员声明为静态成员,静态成员与类关联在一起。静态成员函数不与任何对象绑定在一起,不含this指针,不能声明成为const的。类可以使用作用域运算符来访问静态成员:类名::静态成员,成员函数不用通过作用域运算符就能直接使用静态成员。

21.static只能在类内部声明。由于静态成员不属于类的任何一个对象,所以他们并不是在创建类的对象时被定义的,这意味着他们不是由构造函数初始化的。一般来说,我们不能在类的内部初始化静态成员,必须在类的外部定义和初始化每个静态成员。类似于全局变量,静态数据成员定义在任何函数之外,因此一旦被定义,就将一直存在于程序的整个生命周期当中。

猜你喜欢

转载自blog.csdn.net/AC_Gibson/article/details/50478257
今日推荐