C++ primer 读书笔记 第七章 01 定义抽象数据类型

定义成员函数

成员函数的声明必须在类的内部,它的定义既可以在类的内部也可以在类的外部。

struct Sales_data  
{  
    std::string isbn() const { return bookNo; }  
    double arv_price() const;  
};
  • isbn函数的定义和声明都在类的内部
  • arv_price函数只在类的内部做了声明,需要在其它地方进行实现

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

this

total.isbn();

我们使用点运算符来访问total对象的isbn成员,然后调用它

当isbn返回bookNo时,实际上它隐式地返回total.bookNo

那么成员函数是如何访问调用它的对象的呢?

类的成员函数通过一个名为this的额外的隐式参数来访问调用它的那个对象

当我们调用一个成员函数时,用请求该函数的对象地址初始化this

这个操作是隐式进行的,this形参也是隐式的

this是一个常量指针

const成员函数

this指针虽然是隐式定义的,但是它也需要遵循初始化规则。

默认情况下,this是指向类的非常量对象的常量指针。

因此,我们无法将类的常量对象的地址赋值给this指针(无法避免通过this修改对象成员的值)。这和普通指针的赋值规则是一致的。

在isbn的实现中,它也只是读取了数据成员的值,并未对成员进行任何修改。因此它是可以被类的常量对象调用的。

基于以上原因,如果将隐式的this形参声明为指向常量对象的常量指针,会大大提高isbn函数的适用范围。(c++允许将非常量赋值给指向常量的指针)

但是this是隐式的,因此通过在参数列表后添加const关键字来完成这个声明。

一旦给成员函数添加了const声明,则在函数体内只能访问成员,不能修改成员的值。

如果成员被声明成常量成员函数,那么它的定义也必须在参数列表后明确指定const属性

double Saled_data::avg_price() const  
{  
    return units_sold;  
}

这里的const不能省略

在类外定义成员函数时,带上类名和作用域运算符是很有必要的,一旦编译器看到这个函数名,就能理解剩余的代码是位于类的作用域内

返回this对象

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

该函数是在模仿运算符+=,而内置的赋值运算符把它的左侧运算对象当成左值返回,为了和它保持一致,combine函数返回引用类型

total.combine(trans);

返回的是total的引用

定义类相关的非成员函数

类的设计者常常需要定义一些辅助函数,尽管这些函数定义的操作从概念上来说属于类的接口的一部分,但它们实际并不属于类本身。

这里需要注意的是,这些非成员函数出现的位置

struct Sales_data  
{  

};  

Sales_data add(const Sales_data&, const Sales_data&);  
std::istream &read(std::istream&, Sales_data&);

构造函数

  • 每个类都分别定义了它的对象被初始化的方式
  • 由一个或多个构造函数来控制对象的初始化过程
  • 无论何时只要类的对象被创建,就会执行构造函数

构造函数是一种特殊的成员函数

  • 它的名字和类名相同
  • 没有返回类型
  • 不能被声明成const的

当我们创建类的一个const对象时,直到构造函数完成初始化过程,对象才能真正取得其”常量”属性。因此,构造函数在const对象的构造过程中可以向其写值。

形如,

Sales_data total;  
Sales_data trans;

的对象定义过程,我们没有在定义对象时为其提供初始值,它们执行的是默认初始化。

类通过一个特殊的构造函数来控制默认初始化的过程,称为默认构造函数

默认构造函数无须任何实参

如果我们的类没有显式地定义构造函数,编译器就会为我们隐式地定义一个默认构造函数。

由编译器创建的构造函数称为合成的构造函数。

在下列情况下,最好能提供默认构造函数

  1. 只有当类没有声明任何构造函数时,编译器才会自动地生成默认构造函数。因此,如果我们为类定义了一些其他构造函数,最好也能提供默认构造函数,否则类没有默认构造函数。
  2. 合成的默认构造函数可能执行错误的操作。比如默认初始化数据成员时,可能得到未定义的值
  3. 如果类中包含其它类类型的成员,且这个成员的类型没有默认构造函数,我们必须自定义默认构造函数,否则该类没有可用的默认构造函数。
strcut Sales_data  
{  
    Sales_data() = default;  
    Sales_data(const std::string &s):bookNo(s){}  
};

=default

这里我们定义了其他构造函数,因此编译器不会为我们生成合成的默认构造函数。

所以自定义了一个默认构造函数,且在它的后面添加了=default关键字

=default要求编译器生成默认构造函数,使用和合成的默认构造函数一样的规则来初始化成员。

=default可以和声明一起出现,也可以和定义一起出现在类的外部。

如果出现在类的内部,则默认构造函数是内联的。

构造函数初始值列表

Sales_data(const std::string &s):bookNo(s){}

:bookNo(s)这部分函数称为构造函数初始值列表,它的作用是使用构造函数的实参完成成员的初始化过程。

没有出现在初始值列表中的成员,也有三个机会完成初始化

  1. 构造函数的函数体
  2. 类内初始值
  3. 默认初始化

猜你喜欢

转载自blog.csdn.net/zpznba/article/details/84675361